EPICS Home

Experimental Physics and Industrial Control System


 
2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] lp:~epics-core/epics-base/server-side-plugins into lp:epics-base
From: mdavidsaver <[email protected]>
To: [email protected], Ralph Lange <[email protected]>
Date: Wed, 30 May 2012 18:11:19 -0000
mdavidsaver has proposed merging lp:~epics-core/epics-base/server-side-plugins into lp:epics-base.

Requested reviews:
  Ralph Lange (ralph-lange)
  EPICS Core Developers (epics-core)

For more details, see:
https://code.launchpad.net/~epics-core/epics-base/server-side-plugins/+merge/108028

This branch takes the changes from

lp:~ralph-lange/epics-base/server-side-plugins

And updates them to the current 3.15 branch.  So when reviewing the changes please check also for unintentionally reverted changes from the 3.15 branch.
-- 
The attached diff has been truncated due to its size.
https://code.launchpad.net/~epics-core/epics-base/server-side-plugins/+merge/108028
Your team EPICS Core Developers is requested to review the proposed merge of lp:~epics-core/epics-base/server-side-plugins into lp:epics-base.
=== modified file '.bzrignore'
--- .bzrignore	2009-12-23 21:06:44 +0000
+++ .bzrignore	2012-05-30 18:10:27 +0000
@@ -6,3 +6,6 @@
 ./include
 ./templates
 **/O.*
+./.cproject
+./.project
+./.settings

=== modified file 'configure/RULES.Db'
--- configure/RULES.Db	2012-05-29 21:44:49 +0000
+++ configure/RULES.Db	2012-05-30 18:10:27 +0000
@@ -397,12 +397,12 @@
 
 %_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd 
 	@$(RM) $@ $*.tmp
-	$(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp
+	$(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp
 	$(MV) $*.tmp $@
 
 %_registerRecordDeviceDriver.cpp: %.dbd 
 	@$(RM) $@ $*.tmp
-	$(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp
+	$(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp
 	$(MV) $*.tmp $@
 
 .PRECIOUS: %_registerRecordDeviceDriver.cpp

=== modified file 'src/Makefile'
--- src/Makefile	2012-03-14 20:27:40 +0000
+++ src/Makefile	2012-05-30 18:10:27 +0000
@@ -69,6 +69,10 @@
 DIRS += std
 std_DEPEND_DIRS = ioc libCom/RTEMS
 
+DIRS += ioc/db/filters/test
+ioc/db/filters/test_DEPEND_DIRS = ioc std libCom/RTEMS
+
+
 
 include $(TOP)/configure/RULES_DIRS
 

=== modified file 'src/ioc/as/asDbLib.c'
--- src/ioc/as/asDbLib.c	2012-05-03 17:19:34 +0000
+++ src/ioc/as/asDbLib.c	2012-05-30 18:10:27 +0000
@@ -6,7 +6,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:  Marty Kraimer Date:    02-11-94*/
 
@@ -27,7 +27,7 @@
 #include "caeventmask.h"
 #include "callback.h"
 #include "dbStaticLib.h"
-#include "dbAddr.h"
+#include "dbChannel.h"
 #include "dbAccess.h"
 #include "db_field_log.h"
 #include "dbEvent.h"
@@ -40,7 +40,7 @@
 static char	*psubstitutions=NULL;
 static epicsThreadId	asInitTheadId=0;
 static int	firstTime = TRUE;
-
+
 static long asDbAddRecords(void)
 {
     DBENTRY	dbentry;
@@ -98,7 +98,7 @@
     }
     return(0);
 }
-
+
 static void asSpcAsCallback(struct dbCommon *precord)
 {
     asChangeGroup(&precord->asp, precord->asg);
@@ -109,7 +109,7 @@
     int *firstTime = (int *)arg;
     *firstTime = FALSE;
 }
-    
+
 static long asInitCommon(void)
 {
     long	status;
@@ -117,7 +117,7 @@
     int		wasFirstTime = firstTime;
     static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT;
 
-    
+
     epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime);
     if(wasFirstTime) {
         if(!pacf) return(0); /*access security will NEVER be turned on*/
@@ -148,7 +148,7 @@
 {
     return(asInitCommon());
 }
-
+
 static void wdCallback(void *arg)
 {
     ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;
@@ -195,23 +195,15 @@
     }
     return(0);
 }
-
-int epicsShareAPI asDbGetAsl(void *paddress)
+
+int epicsShareAPI asDbGetAsl(struct dbChannel *chan)
 {
-    DBADDR	*paddr = paddress;
-    dbFldDes	*pflddes;
-
-    pflddes = paddr->pfldDes;
-    return((int)pflddes->as_level);
+    return dbChannelFldDes(chan)->as_level;
 }
 
-void * epicsShareAPI asDbGetMemberPvt(void *paddress)
+void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan)
 {
-    DBADDR	*paddr = paddress;
-    dbCommon	*precord;
-
-    precord = paddr->precord;
-    return((void *)precord->asp);
+    return dbChannelRecord(chan)->asp;
 }
 
 static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status)
@@ -259,7 +251,7 @@
     }
     return(0);
 }
-
+
 static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp)
 {
     dbCommon	*precord;

=== modified file 'src/ioc/as/asDbLib.h'
--- src/ioc/as/asDbLib.h	2010-10-05 19:27:37 +0000
+++ src/ioc/as/asDbLib.h	2012-05-30 18:10:27 +0000
@@ -5,12 +5,12 @@
 *     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.
 \*************************************************************************/
 /* share/epicsH/dbAsLib.h	*/
 /*  $Revision-Id$ */
 /* Author:  Marty Kraimer Date:    02-23-94*/
-
+
 #ifndef INCdbAsLibh
 #define INCdbAsLibh
 
@@ -22,6 +22,8 @@
     long	status;
 } ASDBCALLBACK;
 
+struct dbChannel;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -30,8 +32,8 @@
 epicsShareFunc int epicsShareAPI asSetSubstitutions(const char *substitutions);
 epicsShareFunc int epicsShareAPI asInit(void);
 epicsShareFunc int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback);
-epicsShareFunc int epicsShareAPI asDbGetAsl( void *paddr);
-epicsShareFunc void *  epicsShareAPI asDbGetMemberPvt( void *paddr);
+epicsShareFunc int epicsShareAPI asDbGetAsl(struct dbChannel *chan);
+epicsShareFunc void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan);
 epicsShareFunc int epicsShareAPI asdbdump(void);
 epicsShareFunc int epicsShareAPI asdbdumpFP(FILE *fp);
 epicsShareFunc int epicsShareAPI aspuag(const char *uagname);

=== modified file 'src/ioc/db/Makefile'
--- src/ioc/db/Makefile	2011-11-14 23:38:36 +0000
+++ src/ioc/db/Makefile	2012-05-30 18:10:27 +0000
@@ -17,9 +17,12 @@
 INC += dbCa.h
 INC += dbAddr.h
 INC += dbBkpt.h
+INC += dbChannel.h
 INC += dbConvert.h
 INC += dbConvertFast.h
+INC += dbExtractArray.h
 INC += dbEvent.h
+INC += dbLink.h
 INC += dbLock.h
 INC += dbNotify.h
 INC += dbScan.h
@@ -30,6 +33,8 @@
 INC += initHooks.h
 INC += recGbl.h
 INC += dbIocRegister.h
+INC += chfPlugin.h
+INC += dbState.h
 INC += db_access_routines.h
 INC += db_convert.h
 
@@ -50,11 +55,17 @@
 DBDINC += $(basename $(menuGlobal_DBD))
 DBDINC += dbCommon
 
+DBD_INSTALLS += ../db/filters/filters.dbd
+
+
 dbCore_SRCS += dbLock.c
 dbCore_SRCS += dbAccess.c
 dbCore_SRCS += dbBkpt.c
+dbCore_SRCS += dbChannel.c
 dbCore_SRCS += dbConvert.c
 dbCore_SRCS += dbFastLinkConv.c
+dbCore_SRCS += dbExtractArray.c
+dbCore_SRCS += dbLink.c
 dbCore_SRCS += dbNotify.c
 dbCore_SRCS += dbScan.c
 dbCore_SRCS += dbEvent.c
@@ -74,4 +85,13 @@
 dbCore_SRCS += dbContextReadNotifyCache.cpp
 dbCore_SRCS += templateInstances.cpp
 dbCore_SRCS += dbIocRegister.c
+dbCore_SRCS += chfPlugin.c
+dbCore_SRCS += dbState.c
+
+SRC_DIRS += $(IOCDIR)/db/filters
+
+dbCore_SRCS += ts.c
+dbCore_SRCS += dbnd.c
+dbCore_SRCS += arr.c
+dbCore_SRCS += sync.c
 

=== added file 'src/ioc/db/chfPlugin.c'
--- src/ioc/db/chfPlugin.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/chfPlugin.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,641 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+/* Based on the linkoptions utility by Michael Davidsaver (BNL) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+
+#include <dbDefs.h>
+#include <dbStaticLib.h>
+#include <epicsTypes.h>
+#include <epicsString.h>
+#include <errlog.h>
+#include <shareLib.h>
+
+#define epicsExportSharedSymbols
+#include "chfPlugin.h"
+
+#ifndef HUGE_VALF
+#  define HUGE_VALF HUGE_VAL
+#endif
+#ifndef HUGE_VALL
+#  define HUGE_VALL (-(HUGE_VAL))
+#endif
+
+/*
+ * Data for a chfPlugin
+ */
+typedef struct chfPlugin {
+    const chfPluginArgDef *opts;
+    size_t nopts;
+    epicsUInt32 *required;
+    const chfPluginIf *pif;
+} chfPlugin;
+
+/*
+ * Parser state data for a chfFilter (chfPlugin instance)
+ */
+typedef struct chfFilter {
+    const chfPlugin *plugin;
+    epicsUInt32 *found;
+    void *puser;
+    epicsInt16 nextParam;
+} chfFilter;
+
+/* Data types we get from the parser */
+typedef enum chfPluginType {
+    chfPluginTypeBool,
+    chfPluginTypeInt,
+    chfPluginTypeDouble,
+    chfPluginTypeString
+} chfPluginType;
+
+/*
+ * Convert the (long) integer value 'val' to the type named in 'opt->optType'
+ * and store the result at 'user + opt->offset'.
+ */
+static int store_integer_value(const chfPluginArgDef *opt, void *user, long val)
+{
+    long *ival;
+    int *eval;
+    const chfPluginEnumType *emap;
+    double *dval;
+    char *sval;
+    char buff[22];              /* 2^64 = 1.8e+19, so 20 digits plus sign max */
+
+/*    printf("Got an integer for %s (type %d): %ld\n", opt->name, opt->optType, val); */
+
+    if (!opt->convert && opt->optType != chfPluginArgInt32) {
+        return -1;
+    }
+
+    switch (opt->optType) {
+    case chfPluginArgInt32:
+        ival = (long*) ((char*)user + opt->offset);
+        *ival = val;
+        break;
+    case chfPluginArgBoolean:
+        eval = (int*) ((char*)user + opt->offset);
+        *eval = !!val;
+        break;
+    case chfPluginArgDouble:
+        dval = (double*) ((char*)user + opt->offset);
+        *dval = (double) val;
+        break;
+    case chfPluginArgString:
+        sval = ((char*)user + opt->offset);
+        sprintf(buff, "%ld", val);
+        if (strlen(buff) > opt->size-1) {
+            return -1;
+        }
+        strncpy(sval, buff, opt->size-1);
+        sval[opt->size-1]='\0';
+        break;
+    case chfPluginArgEnum:
+        eval = (int*) ((char*)user + opt->offset);
+        for (emap = opt->enums; emap && emap->name; emap++) {
+            if (val == emap->value) {
+                *eval = val;
+                break;
+            }
+        }
+        if (!emap || !emap->name) {
+            return -1;
+        }
+        break;
+    case chfPluginArgInvalid:
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Convert the (int) boolean value 'val' to the type named in 'opt->optType'
+ * and store the result at 'user + opt->offset'.
+ */
+static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val)
+{
+    long *ival;
+    int *eval;
+    double *dval;
+    char *sval;
+
+/*    printf("Got a boolean for %s (type %d): %d\n", opt->name, opt->optType, val); */
+
+    if (!opt->convert && opt->optType != chfPluginArgBoolean) {
+        return -1;
+    }
+
+    switch (opt->optType) {
+    case chfPluginArgInt32:
+        ival = (long*) ((char*)user + opt->offset);
+        *ival = val;
+        break;
+    case chfPluginArgBoolean:
+        eval = (int*) ((char*)user + opt->offset);
+        *eval = val;
+        break;
+    case chfPluginArgDouble:
+        dval = (double*) ((char*)user + opt->offset);
+        *dval = val ? 1. : 0.;
+        break;
+    case chfPluginArgString:
+        sval = ((char*)user + opt->offset);
+        if ((val ? 4 : 5) > opt->size-1) {
+            return -1;
+        }
+        strncpy(sval, (val ? "true" : "false"), opt->size-1);
+        sval[opt->size-1]='\0';
+        break;
+    case chfPluginArgEnum:
+    case chfPluginArgInvalid:
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Convert the double value 'val' to the type named in 'opt->optType'
+ * and store the result at 'user + opt->offset'.
+ */
+static int store_double_value(const chfPluginArgDef *opt, void *user, double val)
+{
+    long *ival;
+    int *eval;
+    double *dval;
+    char *sval;
+    int i;
+
+/*
+    printf("Got a double for %s (type %d, convert: %s): %g\n",
+           opt->name, opt->optType, opt->convert?"yes":"no", val);
+*/
+    if (!opt->convert && opt->optType != chfPluginArgDouble) {
+        return -1;
+    }
+
+    switch (opt->optType) {
+    case chfPluginArgInt32:
+        if (val < LONG_MIN || val > LONG_MAX) {
+            return -1;
+        }
+        ival = (long*) ((char*)user + opt->offset);
+        *ival = (long) val;
+        break;
+    case chfPluginArgBoolean:
+        eval = (int*) ((char*)user + opt->offset);
+        *eval = (val != 0.) ? 1 : 0;
+        break;
+    case chfPluginArgDouble:
+        dval = (double*) ((char*)user + opt->offset);
+        *dval = val;
+        break;
+    case chfPluginArgString:
+        sval = ((char*)user + opt->offset);
+        if (opt->size <= 8) {       /* Play it safe: 3 exp + 2 sign + 'e' + '.' */
+            return -1;
+        }
+        i = snprintf(sval, opt->size, "%.*g", opt->size - 7, val);
+        if (i >= opt->size) {
+            return -1;
+        }
+        break;
+    case chfPluginArgEnum:
+    case chfPluginArgInvalid:
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Convert the (char*) string value 'val' to the type named in 'opt->optType'
+ * and store the result at 'user + opt->offset'.
+ */
+static int store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len)
+{
+    long *ival;
+    int *eval;
+    const chfPluginEnumType *emap;
+    double *dval;
+    char *sval;
+    char *end;
+    int i;
+
+/*    printf("Got a string for %s (type %d): %.*s\n", opt->name, opt->optType, len, val); */
+
+    if (!opt->convert && opt->optType != chfPluginArgString && opt->optType != chfPluginArgEnum) {
+        return -1;
+    }
+
+    switch (opt->optType) {
+    case chfPluginArgInt32:
+        ival = (long*) ((char*)user + opt->offset);
+        *ival = strtol(val, &end, 0);
+        /* test for the myriad error conditions which strtol may use */
+        if (*ival == LONG_MAX || *ival == LONG_MIN || end == val) {
+            return -1;
+        }
+        break;
+    case chfPluginArgBoolean:
+        eval = (int*) ((char*)user + opt->offset);
+        if (strncasecmp(val, "true", len) == 0) {
+            *eval = 1;
+        } else if (strncasecmp(val, "false", len) == 0) {
+            *eval = 0;
+        } else {
+            i = strtol(val, &end, 0);
+            if (i > INT_MAX || i < INT_MIN || end == val) {
+                return -1;
+            }
+            *eval = !!i;
+        }
+        break;
+    case chfPluginArgDouble:
+        dval = (double*) ((char*)user + opt->offset);
+        *dval = strtod(val, &end);
+        /* Indicates errors in the same manner as strtol */
+        if (*dval==HUGE_VALF||*dval==HUGE_VALL||end==val )
+        {
+            return -1;
+        }
+        break;
+    case chfPluginArgString:
+        i = opt->size-1 < len ? opt->size-1 : len;
+        sval = ((char*)user + opt->offset);
+        strncpy(sval, val, i);
+        sval[i] = '\0';
+        break;
+    case chfPluginArgEnum:
+        eval = (int*) ((char*)user + opt->offset);
+        for (emap = opt->enums; emap && emap->name; emap++) {
+            if (strncmp(emap->name, val, len) == 0) {
+                *eval = emap->value;
+                break;
+            }
+        }
+        if( !emap || !emap->name ) {
+            return -1;
+        }
+        break;
+    case chfPluginArgInvalid:
+        return -1;
+    }
+    return 0;
+}
+
+static void freeInstanceData(chfFilter *f)
+{
+    free(f->found);  /* FIXME: Use a free-list */
+    free(f);         /* FIXME: Use a free-list */
+}
+
+/*
+ * chFilterIf callbacks
+ */
+
+/* First entry point when a new filter instance is created.
+ * All per-instance allocations happen here.
+ */
+static parse_result parse_start(chFilter *filter)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f;
+
+    /* Filter context */
+    /* FIXME: Use a free-list */
+    f = calloc(1, sizeof(chfFilter));
+    if (!f) {
+        fprintf(stderr,"chfFilterCtx calloc failed\n");
+        goto errfctx;
+    }
+    f->nextParam = -1;
+
+    /* Bit array to find missing required keys */
+    /* FIXME: Use a free-list */
+    f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) );
+    if (!f->found) {
+        fprintf(stderr,"chfConfigParseStart: bit array calloc failed\n");
+        goto errbitarray;
+    }
+
+    /* Call the plugin to allocate its structure, it returns NULL on error */
+    if (p->pif->allocPvt) {
+        if ((f->puser = p->pif->allocPvt()) == NULL)
+            goto errplugin;
+    }
+
+    filter->puser = (void*) f;
+
+    return parse_continue;
+
+    errplugin:
+    free(f->found);  /* FIXME: Use a free-list */
+    errbitarray:
+    free(f);         /* FIXME: Use a free-list */
+    errfctx:
+    return parse_stop;
+}
+
+static void parse_abort(chFilter *filter) {
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+
+    /* Call the plugin to tell it we're aborting */
+    if (p->pif->parse_error) p->pif->parse_error(f->puser);
+    if (p->pif->freePvt) p->pif->freePvt(f->puser);
+    freeInstanceData(f);
+}
+
+static parse_result parse_end(chFilter *filter)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+    int i;
+
+    /* Check if all required arguments were supplied */
+    for(i = 0; i < (p->nopts/32)+1; i++) {
+        if ((f->found[i] & p->required[i]) != p->required[i]) {
+            if (p->pif->parse_error) p->pif->parse_error(f->puser);
+            if (p->pif->freePvt) p->pif->freePvt(f->puser);
+            freeInstanceData(f);
+            return parse_stop;
+        }
+    }
+
+    /* Call the plugin to tell it we're done */
+    if (p->pif->parse_ok) {
+        if (p->pif->parse_ok(f->puser)) {
+            if (p->pif->freePvt) p->pif->freePvt(f->puser);
+            freeInstanceData(f);
+            return parse_stop;
+        }
+    }
+
+    return parse_continue;
+}
+
+static parse_result parse_boolean(chFilter *filter, int boolVal)
+{
+    const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
+    chfFilter *f =  (chfFilter*)filter->puser;
+
+    if (f->nextParam < 0 || store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) {
+        return parse_stop;
+    } else {
+        return parse_continue;
+    }
+}
+
+static parse_result parse_integer(chFilter *filter, long integerVal)
+{
+    const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
+    chfFilter *f =  (chfFilter*)filter->puser;
+
+    if (f->nextParam < 0 || store_integer_value(&opts[f->nextParam], f->puser, integerVal)) {
+        return parse_stop;
+    } else {
+        return parse_continue;
+    }
+}
+
+static parse_result parse_double(chFilter *filter, double doubleVal)
+{
+    const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
+    chfFilter *f =  (chfFilter*)filter->puser;
+
+    if (f->nextParam < 0 || store_double_value(&opts[f->nextParam], f->puser, doubleVal)) {
+        return parse_stop;
+    } else {
+        return parse_continue;
+    }
+}
+
+static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen)
+{
+    const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
+    chfFilter *f = (chfFilter*)filter->puser;
+
+    if (f->nextParam < 0 || store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) {
+        return parse_stop;
+    } else {
+        return parse_continue;
+    }
+}
+
+static parse_result parse_start_map(chFilter *filter)
+{
+    return parse_continue;
+}
+
+static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen)
+{
+    const chfPluginArgDef *cur;
+    const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
+    chfFilter *f =  (chfFilter*)filter->puser;
+    int i;
+
+    f->nextParam = -1;
+    for(cur = opts, i = 0; cur && cur->name; cur++, i++) {
+        if (strncmp(key, cur->name, stringLen) == 0) {
+            f->nextParam = i;
+            break;
+        }
+    }
+    if (f->nextParam == -1) {
+        return parse_stop;
+    }
+
+    f->found[i/32] |= 1<<(i%32);
+    return parse_continue;
+}
+
+static parse_result parse_end_map(chFilter *filter)
+{
+    return parse_continue;
+}
+
+static long channel_open(chFilter *filter)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+
+    if (p->pif->channel_open) return p->pif->channel_open(filter->chan, f->puser);
+    else return 0;
+}
+
+static void channel_register_pre(chFilter *filter,
+                                 chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+
+    if (p->pif->channelRegisterPre)
+        p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, probe);
+}
+
+static void channel_register_post(chFilter *filter,
+                                  chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+
+    if (p->pif->channelRegisterPost)
+        p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe);
+}
+
+static void channel_report(chFilter *filter, int level, const unsigned short indent)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f = (chfFilter*) filter->puser;
+
+    if (p->pif->channel_report)
+        p->pif->channel_report(filter->chan, f->puser, level, indent);
+}
+
+static void channel_close(chFilter *filter)
+{
+    chfPlugin *p = (chfPlugin*) filter->plug->puser;
+    chfFilter *f =  (chfFilter*) filter->puser;
+
+    if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser);
+    if (p->pif->freePvt) p->pif->freePvt(f->puser);
+    free(f->found);  /* FIXME: Use a free-list */
+    free(f);         /* FIXME: Use a free-list */
+}
+
+/*
+ * chFilterIf for the wrapper
+ * we just support a simple one-level map, and no arrays
+ */
+static chFilterIf wrapper_fif = {
+    parse_start,
+    parse_abort,
+    parse_end,
+
+    NULL, /* parse_null, */
+    parse_boolean,
+    parse_integer,
+    parse_double,
+    parse_string,
+
+    parse_start_map,
+    parse_map_key,
+    parse_end_map,
+
+    NULL, /* parse_start_array, */
+    NULL, /* parse_end_array, */
+
+    channel_open,
+    channel_register_pre,
+    channel_register_post,
+    channel_report,
+    channel_close
+};
+
+const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def)
+{
+    for(; emap && emap->name; emap++) {
+        if ( i == emap->value ) {
+            return emap->name;
+        }
+    }
+    return def;
+}
+
+int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts)
+{
+    chfPlugin *p;
+    size_t i;
+    const chfPluginArgDef *cur;
+    epicsUInt32 *reqd;
+
+    /* Check and count options */
+    for (i = 0, cur = opts; cur && cur->name; i++, cur++) {
+        switch(cur->optType) {
+        case chfPluginArgInt32:
+            if (cur->size < sizeof(long)) {
+                errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for long (%lu)\n",
+                             key, cur->size, cur->name,
+                             (unsigned long) sizeof(long));
+                return -1;
+            }
+            break;
+        case chfPluginArgBoolean:
+            if (cur->size < 1) {
+                errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for boolean (%lu)\n",
+                             key, cur->size, cur->name,
+                             (unsigned long) sizeof(char));
+                return -1;
+            }
+            break;
+        case chfPluginArgDouble:
+            if (cur->size < sizeof(double)) {
+                errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for double (%lu)\n",
+                             key, cur->size, cur->name,
+                             (unsigned long) sizeof(double));
+                return -1;
+            }
+            break;
+        case chfPluginArgString:
+            if (cur->size < sizeof(char*)) {
+                  /* Catch if someone has given us a char* instead of a char[]
+                   * Also means that char buffers must be >=4.
+                   */
+                errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for string (>= %lu)\n",
+                             key, cur->size, cur->name,
+                             (unsigned long) sizeof(char*));
+                return -1;
+            }
+            break;
+        case chfPluginArgEnum:
+            if (cur->size < sizeof(int)) {
+                errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for enum (%lu)\n",
+                             key, cur->size, cur->name,
+                             (unsigned long) sizeof(int));
+                return -1;
+            }
+            break;
+        case chfPluginArgInvalid:
+            errlogPrintf("Plugin %s: storage type for %s is not defined\n",
+                         key, cur->name);
+            return -1;
+            break;
+        }
+    }
+
+    /* Bit array used to find missing required keys */
+    reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32));
+    if (!reqd) {
+        fprintf(stderr,"Plugin %s: bit array calloc failed\n", key);
+        return -1;
+    }
+
+    for (i = 0, cur = opts; cur && cur->name; i++, cur++) {
+        if (cur->required) reqd[i/32] |= 1 << (i%32);
+    }
+
+    /* Plugin data */
+    p = dbCalloc(1, sizeof(chfPlugin));
+    p->pif = pif;
+    p->opts = opts;
+    p->nopts = i;
+    p->required = reqd;
+
+    dbRegisterFilter(key, &wrapper_fif, p);
+
+    return 0;
+}

=== added file 'src/ioc/db/chfPlugin.h'
--- src/ioc/db/chfPlugin.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/chfPlugin.h	2012-05-30 18:10:27 +0000
@@ -0,0 +1,280 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+/* Based on the linkoptions utility by Michael Davidsaver (BNL) */
+
+#ifndef CHFPLUGIN_H
+#define CHFPLUGIN_H
+
+#include <shareLib.h>
+#include <dbDefs.h>
+#include <epicsTypes.h>
+#include <dbChannel.h>
+
+struct db_field_log;
+
+/** @file chfPlugin.h
+ * @brief Channel filter simplified plugins.
+ *
+ * Utility layer to allow an easier (reduced) interface for
+ * channel filter plugins.
+ *
+ * Parsing the configuration arguments of a channel filter plugin
+ * is done according to an argument description table provided by the plugin.
+ * The parser stores the results directly into a user supplied structure
+ * after appropriate type conversion.
+ *
+ * To specify the arguments, a chfPluginArgDef table must be defined
+ * for the user structure. This table has to be specified when the plugin registers.
+ *
+ * The plugin is responsible to register an init function using
+ * epicsExportRegistrar() and the accompanying registrar() directive in the dbd,
+ * and call chfPluginRegister() from within the init function.
+ *
+ * For example:
+ *
+ * typedef struct myStruct {
+ *   ... other stuff
+ *   epicsUInt32 ival;
+ *   double      dval;
+ *   epicsUInt32 ival2;
+ *   int         enumval;
+ *   char       strval[20];
+ * } myStruct;
+ *
+ * static const
+ * chfPluginEnumType colorEnum[] = { {"Red",1}, {"Green",2}, {"Blue",3}, {NULL,0} };
+ *
+ * static const
+ * chfPluginDef myStructDef[] = {
+ *   chfInt32 (myStruct, ival,    "Integer" , 0, 0),
+ *   chfInt32 (myStruct, ival2,   "Second"  , 1, 0),
+ *   chfDouble(myStruct, dval,    "Double"  , 1, 0),
+ *   chfString(myStruct, strval , "String"  , 1, 0),
+ *   chfEnum  (myStruct, enumval, "Color"   , 1, 0, colorEnum),
+ *   chfPluginEnd
+ * };
+ *
+ * Note: The 4th argument specifies the parameter to be required (1) or optional (0),
+ *       the 5th whether converting to the required type is allowed (1), or
+ *       type mismatches are an error (0).
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Channel filter simplified plugin interface.
+ *
+ * The routines in this structure must be implemented by each filter plugin.
+ */
+typedef struct chfPluginIf {
+
+    /* Memory management */
+    /** @brief Allocate private resources.
+     *
+     * <em>Called before parsing starts.</em>
+     * The plugin should allocate its per-instance structures,
+     * returning a pointer to them or NULL requesting an abort of the operation.
+     *
+     * allocPvt may be set to NULL, if no resource allocation is needed.
+     *
+     * @return Pointer to private structure, NULL if operation is to be aborted.
+     */
+    void * (* allocPvt) (void);
+
+    /** @brief Free private resources.
+     *
+     * <em>Called as part of abort or shutdown.</em>
+     * The plugin should release any resources allocated for this filter;
+     * no further calls through this interface will be made.
+     *
+     * freePvt may be set to NULL, if no resources need to be released.
+     *
+     * @param pvt Pointer to private structure.
+     */
+    void (* freePvt) (void *pvt);
+
+    /* Parameter parsing results */
+    /** @brief A parsing error occurred.
+     *
+     * <em>Called after parsing failed with an error.</em>
+     *
+     * @param pvt Pointer to private structure.
+     */
+    void (* parse_error) (void *pvt);
+
+    /** @brief Configuration has been parsed successfully.
+     *
+     * <em>Called after parsing has finished ok.</em>
+     * The plugin may check the validity of the parsed data,
+     * returning -1 to request an abort of the operation.
+     *
+     * @param pvt Pointer to private structure.
+     * @return 0 for success, -1 if operation is to be aborted.
+     */
+    int (* parse_ok) (void *pvt);
+
+    /* Channel operations */
+    /** @brief Open channel.
+     *
+     * <em>Called as part of the channel connection setup.</em>
+     *
+     * @param chan dbChannel for which the connection is being made.
+     * @param pvt Pointer to private structure.
+     * @return 0 for success, -1 if operation is to be aborted.
+     */
+    long (* channel_open) (dbChannel *chan, void *pvt);
+
+    /** @brief Register callbacks for pre-event-queue operation.
+     *
+     * <em>Called as part of the channel connection setup.</em>
+     *
+     * This function is called to establish the stack of plugins that an event
+     * is passed through between the database and the event queue.
+     *
+     * The plugin must set pe_out to point to its own post-event callback in order
+     * to be called when a data update is sent from the database towards the
+     * event queue.
+     *
+     * The plugin may find out the type of data it will receive by looking at 'probe'.
+     * If the plugin will change the data type and/or size, it must update 'probe'
+     * accordingly.
+     *
+     * @param chan dbChannel for which the connection is being made.
+     * @param pvt Pointer to private structure.
+     * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass
+     *        this plugin).
+     * @param arg_out Argument that must be supplied when calling
+     *        this plugin's post-event callback.
+     */
+    void (* channelRegisterPre) (dbChannel *chan, void *pvt,
+                                 chPostEventFunc **cb_out, void **arg_out,
+                                 db_field_log *probe);
+
+    /** @brief Register callbacks for post-event-queue operation.
+     *
+     * <em>Called as part of the channel connection setup.</em>
+     *
+     * This function is called to establish the stack of plugins that an event
+     * is passed through between the event queue and the final user (CA server or
+     * database access).
+     *
+     * The plugin must set pe_out to point to its own post-event callback in order
+     * to be called when a data update is sent from the event queue towards the
+     * final user.
+     *
+     * The plugin may find out the type of data it will receive by looking at 'probe'.
+     * If the plugin will change the data type and/or size, it must update 'probe'
+     * accordingly.
+     *
+     * @param chan dbChannel for which the connection is being made.
+     * @param pvt Pointer to private structure.
+     * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass
+     *        this plugin).
+     * @param arg_out Argument that must be supplied when calling
+     *        this plugin's post-event callback.
+     */
+    void (* channelRegisterPost) (dbChannel *chan, void *pvt,
+                                  chPostEventFunc **cb_out, void **arg_out,
+                                  db_field_log *probe);
+
+    /** @brief Channel report request.
+     *
+     * <em>Called as part of show... routines.</em>
+     *
+     * @param chan dbChannel for which the report is requested.
+     * @param pvt Pointer to private structure.
+     * @param level Interest level.
+     * @param indent Number of spaces to print before each output line.
+     */
+    void (* channel_report) (dbChannel *chan, void *pvt, int level, const unsigned short indent);
+
+    /* FIXME: More filter routines here ... */
+
+    /** @brief Channel close request.
+     *
+     * <em>Called as part of connection shutdown.</em>
+     * @param chan dbChannel for which the connection is being shut down.
+     * @param pvt Pointer to private structure.
+     */
+    void (* channel_close) (dbChannel *chan, void *pvt);
+
+} chfPluginIf;
+
+typedef enum chfPluginArg {
+    chfPluginArgInvalid=0,
+    chfPluginArgBoolean,
+    chfPluginArgInt32,
+    chfPluginArgDouble,
+    chfPluginArgString,
+    chfPluginArgEnum
+} chfPluginArg;
+
+typedef struct chfPluginEnumType {
+    const char *name;
+    const int value;
+} chfPluginEnumType;
+
+typedef struct chfPluginArgDef {
+    const char * name;
+    chfPluginArg optType;
+    int required:1;
+    int convert:1;
+    epicsUInt32 offset;
+    epicsUInt32 size;
+    const chfPluginEnumType *enums;
+} chfPluginArgDef;
+
+#define chfInt32(Struct, Member, Name, Req, Conv) \
+{Name, chfPluginArgInt32, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
+
+#define chfBoolean(Struct, Member, Name, Req, Conv) \
+{Name, chfPluginArgBoolean, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
+
+#define chfDouble(Struct, Member, Name, Req, Conv) \
+{Name, chfPluginArgDouble, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
+
+#define chfString(Struct, Member, Name, Req, Conv) \
+{Name, chfPluginArgString, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
+
+#define chfEnum(Struct, Member, Name, Req, Conv, Enums) \
+{Name, chfPluginArgEnum, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}
+
+#define chfPluginArgEnd {0}
+
+/* Extra output when parsing and converting */
+#define CHFPLUGINDEBUG 1
+
+/** @brief Return the string associated with Enum index 'i'.
+ *
+ * @param Enums A null-terminated array of string/integer pairs.
+ * @param i An Enum index.
+ * @param def String to be returned when 'i' isn't a valid Enum index.
+ * @return The string associated with 'i'.
+ */
+epicsShareFunc const char* chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def);
+
+/** @brief Register a plugin.
+ *
+ * @param key The plugin name key that clients will use.
+ * @param pif Pointer to the plugin's interface.
+ * @param opts Pointer to the configuration argument description table.
+ */
+epicsShareFunc int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CHFPLUGIN_H

=== modified file 'src/ioc/db/dbAccess.c'
--- src/ioc/db/dbAccess.c	2012-02-20 16:01:04 +0000
+++ src/ioc/db/dbAccess.c	2012-05-30 18:10:27 +0000
@@ -1,21 +1,22 @@
 /*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE 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.
 \*************************************************************************/
-/* dbAccess.c */
-/* $Revision-Id$ */
+
 /*
  *      Original Author: Bob Dalesio
  *      Current Author:  Marty Kraimer
- *      Date:            11-7-90
-*/
+ *                       Andrew Johnson <[email protected]>
+ *                       Ralph Lange <[email protected]>
+ */
 
-
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -46,6 +47,7 @@
 #include "dbAddr.h"
 #include "callback.h"
 #include "dbScan.h"
+#include "dbLink.h"
 #include "dbLock.h"
 #include "dbEvent.h"
 #include "dbConvert.h"
@@ -89,17 +91,6 @@
 
 /* The following is to handle SPC_AS */
 static SPC_ASCALLBACK spcAsCallback = 0;
-
-static void inherit_severity(const struct pv_link *ppv_link,
-    dbCommon *pdest, epicsEnum16 stat, epicsEnum16 sevr)
-{
-    switch(ppv_link->pvlMask&pvlOptMsMode) {
-        case pvlOptNMS: break;
-        case pvlOptMSI: if (sevr < INVALID_ALARM) break;
-        case pvlOptMS:  recGblSetSevr(pdest,LINK_ALARM,sevr); break;
-        case pvlOptMSS: recGblSetSevr(pdest,stat,sevr); break;
-    }
-}
 
 void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func)
 {
@@ -113,7 +104,7 @@
     dbCommon 	*precord = paddr->precord;
     long	status=0;
     long	special=paddr->special;
-	
+
     prset = dbGetRset(paddr);
     if(special<100) { /*global processing*/
 	if((special==SPC_NOMOD) && (pass==0)) {
@@ -139,7 +130,7 @@
     }
     return(0);
 }
-
+
 static void get_enum_strs(DBADDR *paddr, char **ppbuffer,
 	struct rset *prset,long	*options)
 {
@@ -199,7 +190,7 @@
 	*ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size;
 	return;
 }
-
+
 static void get_graphics(DBADDR *paddr, char **ppbuffer,
 	struct rset *prset,long	*options)
 {
@@ -239,7 +230,7 @@
 	}
 	return;
 }
-
+
 static void get_control(DBADDR *paddr, char **ppbuffer,
 	struct rset *prset,long	*options)
 {
@@ -279,7 +270,7 @@
 	}
 	return;
 }
-
+
 static void get_alarm(DBADDR *paddr, char **ppbuffer,
 	struct rset *prset,long	*options)
 {
@@ -324,35 +315,44 @@
 	}
 	return;
 }
-
-static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin)
+
+/*
+ * This code relies on *poriginal being aligned and all increments done by the
+ * blocks only changing the buffer pointer in a way that does not break alignment.
+ */
+static void getOptions(DBADDR *paddr, char **poriginal, long *options,
+        void *pflin)
 {
 	db_field_log	*pfl= (db_field_log *)pflin;
 	struct rset	*prset;
-	short		field_type=paddr->field_type;
+        short		field_type;
 	dbCommon	*pcommon;
 	char		*pbuffer = *poriginal;
 
+        if (!pfl || pfl->type == dbfl_type_rec)
+            field_type = paddr->field_type;
+        else
+            field_type = pfl->field_type;
 	prset=dbGetRset(paddr);
 	/* Process options */
 	pcommon = paddr->precord;
 	if( (*options) & DBR_STATUS ) {
 	    unsigned short *pushort = (unsigned short *)pbuffer;
 
-	    if(pfl!=NULL) {
-		*pushort++ = pfl->stat;
-		*pushort++ = pfl->sevr;
-	    } else {
-		*pushort++ = pcommon->stat;
-		*pushort++ = pcommon->sevr;
-	    }
+            if (!pfl || pfl->type == dbfl_type_rec) {
+                *pushort++ = pcommon->stat;
+                *pushort++ = pcommon->sevr;
+            } else {
+                *pushort++ = pfl->stat;
+                *pushort++ = pfl->sevr;
+            }
 	    *pushort++ = pcommon->acks;
 	    *pushort++ = pcommon->ackt;
 	    pbuffer = (char *)pushort;
 	}
 	if( (*options) & DBR_UNITS ) {
 	    memset(pbuffer,'\0',dbr_units_size);
-	    if( prset && prset->get_units ){ 
+	    if( prset && prset->get_units ){
 		(*prset->get_units)(paddr, pbuffer);
 		pbuffer[DB_UNITS_SIZE-1] = '\0';
 	    } else {
@@ -363,7 +363,7 @@
 	if( (*options) & DBR_PRECISION ) {
 	    memset(pbuffer, '\0', dbr_precision_size);
 	    if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE)
-	    &&  prset && prset->get_precision ){ 
+	    &&  prset && prset->get_precision ){
 		(*prset->get_precision)(paddr,pbuffer);
 	    } else {
 		*options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/
@@ -373,12 +373,12 @@
 	if( (*options) & DBR_TIME ) {
 	    epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
 
-	    if(pfl!=NULL) {
-		*ptime++ = pfl->time.secPastEpoch;
-		*ptime++ = pfl->time.nsec;
-	    } else {
-		*ptime++ = pcommon->time.secPastEpoch;
-		*ptime++ = pcommon->time.nsec;
+            if (!pfl || pfl->type == dbfl_type_rec) {
+                *ptime++ = pcommon->time.secPastEpoch;
+                *ptime++ = pcommon->time.nsec;
+            } else {
+                *ptime++ = pfl->time.secPastEpoch;
+                *ptime++ = pfl->time.nsec;
 	    }
 	    pbuffer = (char *)ptime;
 	}
@@ -392,7 +392,7 @@
 	    get_alarm(paddr, &pbuffer, prset, options);
 	*poriginal = pbuffer;
 }
-
+
 struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr)
 {
 	struct dbFldDes *pfldDes = paddr->pfldDes;
@@ -419,10 +419,10 @@
 
 int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes)
 {
-    if(pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType)
-	return(TRUE);
+    if (pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType)
+        return TRUE;
     else
-	return(FALSE);
+        return FALSE;
 }
 
 int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr)
@@ -430,50 +430,6 @@
     return paddr->pfldDes->indRecordType;
 }
 
-long epicsShareAPI dbGetNelements(const struct link *plink,long *nelements)
-{
-    switch(plink->type) {
-    case CONSTANT:
-	*nelements = 0;
-	return(0);
-    case DB_LINK: {
-	    DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt;
-	    *nelements = paddr->no_elements;
-	    return(0);
-	}
-    case CA_LINK:
-	return(dbCaGetNelements(plink,nelements));
-    default:
-	break;
-    }
-    return(S_db_badField);
-}
-
-int epicsShareAPI dbIsLinkConnected(const struct link *plink)
-{
-    switch(plink->type) {
-	case DB_LINK: return(TRUE);
-	case CA_LINK: return(dbCaIsLinkConnected(plink));
-	default: break;
-    }
-    return(FALSE);
-}
-
-int epicsShareAPI dbGetLinkDBFtype(const struct link *plink)
-{
-    switch(plink->type) {
-	case DB_LINK: 
-	{
-	    DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt;
-
-	    return((int)paddr->field_type);
-	}
-	case CA_LINK: return(dbCaGetLinkDBFtype(plink));
-	default: break;
-    }
-    return(-1);
-}
-
 /*
  *  Process a record if its scan field is passive.
  *     Will notify if processing is complete by callback.
@@ -481,50 +437,15 @@
  */
 long epicsShareAPI dbScanPassive(dbCommon *pfrom, dbCommon *pto)
 {
-    long status;
-	
     /* if not passive just return success */
-    if(pto->scan != 0) return(0);
-
-    if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto);
-    status = dbProcess(pto);
-    return(status);
-}
-
-/*KLUDGE: Following needed so that dbPutLink to PROC field works correctly*/
-long epicsShareAPI dbScanLink(dbCommon *pfrom, dbCommon *pto)
-{
-    long		status;
-    unsigned char	pact;
-
-    if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto);
-    pact = pfrom->pact;
-    pfrom->pact = TRUE;
-    status = dbProcess(pto);
-    pfrom->pact = pact;
-    return(status);
-}
-
-void epicsShareAPI dbScanFwdLink(struct link *plink)
-{
-    dbCommon		*precord;
-    struct pv_link      *pvlink;
-    short               fwdLinkValue;
-
-    if(plink->type!=DB_LINK && plink->type!=CA_LINK) return;
-    pvlink = &plink->value.pv_link;
-    precord = pvlink->precord;
-    if(plink->type==DB_LINK) {
-        dbAddr *paddr = (dbAddr *)plink->value.pv_link.pvt;
-        dbScanPassive(precord,paddr->precord);
-        return;
-    }
-    if(!(pvlink->pvlMask & pvlOptFWD)) return;
-    fwdLinkValue = 1;
-    dbCaPutLink(plink,DBR_SHORT,&fwdLinkValue,1);
-    return;
-}
-
+    if (pto->scan != 0)
+        return 0;
+
+    if (pfrom && pfrom->ppn)
+        dbNotifyAdd(pfrom,pto);
+    return dbProcess(pto);
+}
+
 /*
  *   Process the record.
  *     1.  Check for breakpoints.
@@ -536,121 +457,127 @@
  */
 long epicsShareAPI dbProcess(dbCommon *precord)
 {
-	struct rset	*prset = precord->rset;
-	dbRecordType	*pdbRecordType = precord->rdes;
-	unsigned char	tpro=precord->tpro;
-	long		status = 0;
-        int             *ptrace;
-	int		set_trace=FALSE;
-	dbFldDes	*pdbFldDes;
-        int             callNotifyCompletion = FALSE;
-
-        ptrace = dbLockSetAddrTrace(precord);
+    struct rset *prset = precord->rset;
+    dbRecordType *pdbRecordType = precord->rdes;
+    unsigned char tpro = precord->tpro;
+    long status = 0;
+    int *ptrace;
+    int	set_trace = FALSE;
+    dbFldDes *pdbFldDes;
+    int callNotifyCompletion = FALSE;
+
+    ptrace = dbLockSetAddrTrace(precord);
+    /*
+     *  Note that it is likely that if any changes are made
+     *   to dbProcess() corresponding changes will have to
+     *   be made in the breakpoint handler.
+     */
+
+    /* see if there are any stopped records or breakpoints */
+    if (lset_stack_count != 0) {
         /*
-         *  Note that it is likely that if any changes are made
-         *   to dbProcess() corresponding changes will have to
-         *   be made in the breakpoint handler.
+         *  Check to see if the record should be processed
+         *   and activate breakpoint accordingly.  If this
+         *   function call returns non-zero, skip record
+         *   support and fall out of dbProcess().  This is
+         *   done so that a dbContTask() can be spawned to
+         *   take over record processing for the lock set
+         *   containing a breakpoint.
          */
- 
-        /* see if there are any stopped records or breakpoints */
-        if (lset_stack_count != 0) {
-           /*
-            *  Check to see if the record should be processed
-            *   and activate breakpoint accordingly.  If this
-            *   function call returns non-zero, skip record
-            *   support and fall out of dbProcess().  This is
-            *   done so that a dbContTask() can be spawned to
-            *   take over record processing for the lock set
-            *   containing a breakpoint.
-            */
-            if (dbBkpt(precord))
-                goto all_done;
-        }
-    
-	/* check for trace processing*/
-	if (tpro) {
-                if(*ptrace==0) {
-                        *ptrace = 1;
-			set_trace = TRUE;
-		}
-	}
-
-	/* If already active dont process */
-	if (precord->pact) {
-		unsigned short	monitor_mask;
-
-		if (*ptrace) printf("%s: Active %s\n",
-                        epicsThreadGetNameSelf(), precord->name);
-		/* raise scan alarm after MAX_LOCK times */
-		if (precord->stat==SCAN_ALARM) goto all_done;
-		if (precord->lcnt++ !=MAX_LOCK) goto all_done;
-		if (precord->sevr>=INVALID_ALARM) goto all_done;
-		recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM);
-		monitor_mask = recGblResetAlarms(precord);
-		monitor_mask |= DBE_VALUE|DBE_LOG;
-		pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
-		db_post_events(precord,
-			(void *)(((char *)precord) + pdbFldDes->offset),
-			monitor_mask);
-		goto all_done;
-	}
-        else precord->lcnt = 0;
-
-       /*
-        *  Check the record disable link.  A record will not be
-        *    processed if the value retrieved through this link
-        *    is equal to constant set in the record's disv field.
-        */
-        status = dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0);
-
-	/* if disabled check disable alarm severity and return success */
-	if (precord->disa == precord->disv) {
-		if(*ptrace) printf("%s: Disabled %s\n",
-                        epicsThreadGetNameSelf(), precord->name);
-		/*take care of caching and notifyCompletion*/
-		precord->rpro = FALSE;
-		precord->putf = FALSE;
-                callNotifyCompletion = TRUE;
-		/* raise disable alarm */
-		if (precord->stat==DISABLE_ALARM) goto all_done;
-		precord->sevr = precord->diss;
-		precord->stat = DISABLE_ALARM;
-		precord->nsev = 0;
-		precord->nsta = 0;
-		db_post_events(precord, &precord->stat, DBE_VALUE);
-		db_post_events(precord, &precord->sevr, DBE_VALUE);
-		pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
-		db_post_events(precord,
-			(void *)(((char *)precord) + pdbFldDes->offset),
-			DBE_VALUE|DBE_ALARM);
-		goto all_done;
-	}
-
-	/* locate record processing routine */
-        /* put this in iocInit() !!! */
-	if (!(prset=precord->rset) || !(prset->process)) {
-                callNotifyCompletion = TRUE;
-		precord->pact=1;/*set pact TRUE so error is issued only once*/
-		recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess");
-		status = S_db_noRSET;
-		if (*ptrace) printf("%s: No RSET for %s\n",
-				epicsThreadGetNameSelf(), precord->name);
-		goto all_done;
-	}
-	if(*ptrace) printf("%s: Process %s\n",
-			epicsThreadGetNameSelf(), precord->name);
-	/* process record */
-	status = (*prset->process)(precord);
-        /* Print record's fields if PRINT_MASK set in breakpoint field */
-        if (lset_stack_count != 0) {
-                dbPrint(precord);
-        }
+        if (dbBkpt(precord))
+            goto all_done;
+    }
+
+    /* check for trace processing*/
+    if (tpro) {
+        if(*ptrace==0) {
+            *ptrace = 1;
+            set_trace = TRUE;
+        }
+    }
+
+    /* If already active dont process */
+    if (precord->pact) {
+        unsigned short	monitor_mask;
+
+        if (*ptrace)
+            printf("%s: Active %s\n",
+                    epicsThreadGetNameSelf(), precord->name);
+        /* raise scan alarm after MAX_LOCK times */
+        if (precord->stat==SCAN_ALARM) goto all_done;
+        if (precord->lcnt++ !=MAX_LOCK) goto all_done;
+        if (precord->sevr>=INVALID_ALARM) goto all_done;
+        recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM);
+        monitor_mask = recGblResetAlarms(precord);
+        monitor_mask |= DBE_VALUE|DBE_LOG;
+        pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
+        db_post_events(precord,
+                (void *)(((char *)precord) + pdbFldDes->offset),
+                monitor_mask);
+        goto all_done;
+    }
+    else precord->lcnt = 0;
+
+    /*
+     *  Check the record disable link.  A record will not be
+     *    processed if the value retrieved through this link
+     *    is equal to constant set in the record's disv field.
+     */
+    status = dbGetLink(&precord->sdis, DBR_SHORT, &precord->disa, 0, 0);
+
+    /* if disabled check disable alarm severity and return success */
+    if (precord->disa == precord->disv) {
+        if(*ptrace)
+            printf("%s: Disabled %s\n",
+                    epicsThreadGetNameSelf(), precord->name);
+
+        /*take care of caching and notifyCompletion*/
+        precord->rpro = FALSE;
+        precord->putf = FALSE;
+        callNotifyCompletion = TRUE;
+
+        /* raise disable alarm */
+        if (precord->stat==DISABLE_ALARM) goto all_done;
+        precord->sevr = precord->diss;
+        precord->stat = DISABLE_ALARM;
+        precord->nsev = 0;
+        precord->nsta = 0;
+        db_post_events(precord, &precord->stat, DBE_VALUE);
+        db_post_events(precord, &precord->sevr, DBE_VALUE);
+        pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
+        db_post_events(precord,
+                (void *)(((char *)precord) + pdbFldDes->offset),
+                DBE_VALUE|DBE_ALARM);
+        goto all_done;
+    }
+
+    /* locate record processing routine */
+    /* FIXME: put this in iocInit() !!! */
+    if (!(prset=precord->rset) || !(prset->process)) {
+        callNotifyCompletion = TRUE;
+        precord->pact=1;/*set pact TRUE so error is issued only once*/
+        recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess");
+        status = S_db_noRSET;
+        if (*ptrace)
+            printf("%s: No RSET for %s\n",
+                    epicsThreadGetNameSelf(), precord->name);
+        goto all_done;
+    }
+    if(*ptrace)
+        printf("%s: Process %s\n",
+                epicsThreadGetNameSelf(), precord->name);
+    /* process record */
+    status = (*prset->process)(precord);
+    /* Print record's fields if PRINT_MASK set in breakpoint field */
+    if (lset_stack_count != 0) {
+        dbPrint(precord);
+    }
 all_done:
-	if (set_trace) *ptrace = 0;
-	if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord);
-	return(status);
+    if (set_trace) *ptrace = 0;
+    if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord);
+    return(status);
 }
-
+
 /*
  *  Fill out a database structure (*paddr) for
  *    a record given by the name "pname."
@@ -726,59 +653,7 @@
     dbFinishEntry(&dbEntry);
     return status;
 }
-
-/* JOH 10-19-04 */
-static char * dbCopyInNameComponentOfPV (
-    char * pBuf, unsigned bufLen, const char * pComponent )
-{
-    unsigned compLen = strlen ( pComponent );
-    if ( compLen < bufLen ) {
-        strcpy ( pBuf, pComponent );
-        return pBuf + compLen;
-    }
-    else {
-        unsigned reducedSize = bufLen - 1u;
-        strncpy ( pBuf, pComponent, reducedSize );
-        pBuf[reducedSize] = '\0';
-        return pBuf + reducedSize;
-    }
-}
-/*
- *  Copies PV name into pBuf of length bufLen
- *
- *    Returns the number of bytes written to pBuf
- *      not including null termination.
- *  JOH 10-19-04 
- */
-unsigned dbNameOfPV (
-    const dbAddr * paddr, char * pBuf, unsigned bufLen )
-{
-    dbFldDes * pfldDes = paddr->pfldDes;
-    char * pBufTmp = pBuf;
-    if ( bufLen == 0u ) {
-        return 0u;
-    }
-    pBufTmp = dbCopyInNameComponentOfPV ( 
-        pBufTmp, bufLen, paddr->precord->name );
-    pBufTmp = dbCopyInNameComponentOfPV ( 
-        pBufTmp, bufLen - ( pBufTmp - pBuf ), "." );
-    pBufTmp = dbCopyInNameComponentOfPV ( 
-        pBufTmp, bufLen - ( pBufTmp - pBuf ), pfldDes->name );
-    return pBufTmp - pBuf;
-}
-/*
- *    Returns the number of bytes in the PV name
- *      not including null termination.
- *    JOH 10-19-04 
- */
-unsigned dbNameSizeOfPV ( const dbAddr * paddr )
-{
-    unsigned recNameLen = strlen ( paddr->precord->name );
-    dbFldDes * pfldDes = paddr->pfldDes;
-    unsigned fieldNameLen = strlen ( pfldDes->name );
-    return recNameLen + fieldNameLen + 1;
-}
-
+
 long epicsShareAPI dbValueSize(short dbr_type)
 {
     /* sizes for value associated with each DBR request type */
@@ -826,222 +701,117 @@
     return dbReadDatabase(&pdbbase, file, 0, subs);
 }
 
-
-long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType,
-    void *pbuffer, long *poptions, long *pnRequest)
-{
-    long status = 0;
-
-    if (plink->type == CONSTANT) {
-        if (poptions) *poptions = 0;
-        if (pnRequest) *pnRequest = 0;
-    } else if (plink->type == DB_LINK) {
-        struct pv_link *ppv_link = &(plink->value.pv_link);
-        DBADDR         *paddr = ppv_link->pvt;
-        dbCommon       *precord = plink->value.pv_link.precord;
-
-        /* scan passive records with links that are process passive  */
-        if (ppv_link->pvlMask&pvlOptPP) {
-            dbCommon *pfrom = paddr->precord;
-            unsigned char pact;
-
-            pact = precord->pact;
-            precord->pact = TRUE;
-            status = dbScanPassive(precord,pfrom);
-            precord->pact = pact;
-            if (status) return status;
-        }
-        if(precord!= paddr->precord) {
-            inherit_severity(ppv_link,precord,paddr->precord->stat,paddr->precord->sevr);
-        }
-
-        if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
-            status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
-        } else {
-            unsigned short dbfType = paddr->field_type;
-            long       no_elements = paddr->no_elements;
-
-            if (dbrType < 0 || dbrType > DBR_ENUM ||
-                dbfType > DBF_DEVICE) {
-                status = S_db_badDbrtype;
-                recGblRecordError(status, precord, "GetLinkValue Failed");
-                recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
-                return status;
-            }
-            /*  attempt to make a fast link */
-            if ((!poptions || *poptions == 0) &&
-                no_elements == 1 &&
-                (!pnRequest || *pnRequest == 1) &&
-                paddr->special != SPC_ATTRIBUTE) {
-                ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
-                status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
-            } else {
-                ppv_link->getCvt = 0;
-                status = dbGet(paddr, dbrType, pbuffer, poptions, pnRequest, NULL);
-            }
-        }
-        ppv_link->lastGetdbrType = dbrType;
-        if (status) {
-            recGblRecordError(status, precord, "dbGetLinkValue");
-            recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
-        }
-    } else if (plink->type == CA_LINK) {
-        struct dbCommon *precord = plink->value.pv_link.precord;
-        const struct pv_link *pcalink = &plink->value.pv_link;
-        epicsEnum16 sevr, stat;
-
-        status=dbCaGetLink(plink,dbrType,pbuffer,&stat,&sevr,pnRequest);
-        if (status) {
-            recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
-        } else {
-            inherit_severity(pcalink,precord,stat,sevr);
-        }
-        if (poptions) *poptions = 0;
-    } else {
-        cantProceed("dbGetLinkValue: Illegal link type");
-    }
-    return status;
-}
-
-long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType,
-    const void *pbuffer, long nRequest)
-{
-    long status = 0;
-
-    if (plink->type == DB_LINK) {
-        struct dbCommon *psource = plink->value.pv_link.precord;
-        struct pv_link *ppv_link = &plink->value.pv_link;
-        DBADDR *paddr = (DBADDR *)ppv_link->pvt;
-        dbCommon *pdest = paddr->precord;
-
-        status = dbPut(paddr, dbrType, pbuffer, nRequest);
-        inherit_severity(ppv_link,pdest,psource->nsta,psource->nsev);
-        if (status) return status;
-
-        if (paddr->pfield == (void *)&pdest->proc ||
-            (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
-            /*if dbPutField caused asyn record to process */
-            /* ask for reprocessing*/
-            if (pdest->putf) {
-                pdest->rpro = TRUE;
-            } else { /* otherwise ask for the record to be processed*/
-                status = dbScanLink(psource, pdest);
-            }
-        }
-        if (status)
-            recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM);
-    } else if (plink->type == CA_LINK) {
-        struct dbCommon *psource = plink->value.pv_link.precord;
-
-        status = dbCaPutLink(plink, dbrType, pbuffer, nRequest);
-        if (status < 0)
-            recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM);
-    } else {
-        cantProceed("dbPutLinkValue: Illegal link type");
-    }
-    return status;
-}
-
+
+static long getLinkValue(DBADDR *paddr, short dbrType,
+    char *pbuf, long *nRequest)
+{
+    dbCommon *precord = paddr->precord;
+    dbFldDes *pfldDes = paddr->pfldDes;
+    int maxlen;
+    DBENTRY dbEntry;
+    long status;
+
+    switch (dbrType) {
+    case DBR_STRING:
+        maxlen = MAX_STRING_SIZE - 1;
+        if (nRequest && *nRequest > 1) *nRequest = 1;
+        break;
+
+    case DBR_CHAR:
+    case DBR_UCHAR:
+            if (nRequest && *nRequest > 0) {
+            maxlen = *nRequest - 1;
+            break;
+        }
+        /* else fall through ... */
+    default:
+        return S_db_badDbrtype;
+    }
+
+    dbInitEntry(pdbbase, &dbEntry);
+    status = dbFindRecord(&dbEntry, precord->name);
+    if (!status) status = dbFindField(&dbEntry, pfldDes->name);
+    if (!status) {
+        char *rtnString = dbGetString(&dbEntry);
+
+        strncpy(pbuf, rtnString, --maxlen);
+        pbuf[maxlen] = 0;
+    }
+    dbFinishEntry(&dbEntry);
+    return status;
+}
+
+static long getAttrValue(DBADDR *paddr, short dbrType,
+        char *pbuf, long *nRequest)
+{
+    int maxlen;
+
+    if (!paddr->pfield) return S_db_badField;
+
+    switch (dbrType) {
+    case DBR_STRING:
+        maxlen = MAX_STRING_SIZE - 1;
+        if (nRequest && *nRequest > 1) *nRequest = 1;
+        break;
+
+    case DBR_CHAR:
+    case DBR_UCHAR:
+            if (nRequest && *nRequest > 0) {
+            maxlen = *nRequest - 1;
+            break;
+        }
+        /* else fall through ... */
+    default:
+        return S_db_badDbrtype;
+    }
+
+    strncpy(pbuf, paddr->pfield, --maxlen);
+    pbuf[maxlen] = 0;
+    return 0;
+}
+
 long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType,
     void *pbuffer, long *options, long *nRequest, void *pflin)
 {
-    short dbfType = paddr->field_type;
     dbCommon *precord = paddr->precord;
     long status = 0;
 
     dbScanLock(precord);
-    if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
-        DBENTRY dbEntry;
-        dbFldDes *pfldDes = paddr->pfldDes;
-        char *rtnString;
-        char *pbuf = (char *)pbuffer;
-        int maxlen;
-
-        if (options && (*options))
-            getOptions(paddr, &pbuf, options, pflin);
-        if (nRequest && *nRequest == 0) goto done;
-
-        switch (dbrType) {
-        case DBR_STRING:
-            maxlen = MAX_STRING_SIZE - 1;
-            if (nRequest && *nRequest > 1) *nRequest = 1;
-            break;
-
-        case DBR_CHAR:
-        case DBR_UCHAR:
-            if (nRequest && *nRequest > 0) {
-                maxlen = *nRequest - 1;
-                break;
-            }
-            /* else fall through ... */
-        default:
-            status = S_db_badDbrtype;
-            goto done;
-        }
-
-        dbInitEntry(pdbbase, &dbEntry);
-        status = dbFindRecord(&dbEntry, precord->name);
-        if (!status) status = dbFindField(&dbEntry, pfldDes->name);
-        if (!status) {
-            rtnString = dbGetString(&dbEntry);
-            strncpy(pbuf, rtnString, maxlen);
-            pbuf[maxlen] = 0;
-        }
-        dbFinishEntry(&dbEntry);
-    } else {
-        status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
-    }
-done:
+    status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
     dbScanUnlock(precord);
     return status;
 }
-
+
 long epicsShareAPI dbGet(DBADDR *paddr, short dbrType,
     void *pbuffer, long *options, long *nRequest, void *pflin)
 {
+    char *pbuf = pbuffer;
     db_field_log *pfl = (db_field_log *)pflin;
-    short field_type  = paddr->field_type;
-    long no_elements  = paddr->no_elements;
+    short field_type;
+    long no_elements;
     long offset;
     struct rset *prset;
     long status = 0;
 
-    if (options && *options) {
-        char *pbuf = pbuffer;
-
+    if (options && *options)
         getOptions(paddr, &pbuf, options, pflin);
-        pbuffer = pbuf;
-    }
-    if (nRequest && *nRequest == 0) return 0;
-
-    if (paddr->special == SPC_ATTRIBUTE) {
-        char *pbuf = pbuffer;
-        int maxlen;
-
-        if (!paddr->pfield) return S_db_badField;
-
-        switch (dbrType) {
-        case DBR_STRING:
-            maxlen = MAX_STRING_SIZE - 1;
-            if (nRequest && *nRequest > 1) *nRequest = 1;
-            break;
-
-        case DBR_CHAR:
-        case DBR_UCHAR:
-            if (nRequest && *nRequest > 0) {
-                maxlen = *nRequest - 1;
-                break;
-            }
-            /* else fall through ... */
-        default:
-            return S_db_badDbrtype;
-        }
-
-        strncpy(pbuf, (char *)paddr->pfield, maxlen);
-        pbuf[maxlen] = 0;
+    if (nRequest && *nRequest == 0)
         return 0;
+
+    if (!pfl || pfl->type == dbfl_type_rec) {
+        field_type  = paddr->field_type;
+        no_elements = paddr->no_elements;
+    } else {
+        field_type  = pfl->field_type;
+        no_elements = pfl->no_elements;
     }
 
+    if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK)
+        return getLinkValue(paddr, dbrType, pbuf, nRequest);
+
+    if (paddr->special == SPC_ATTRIBUTE)
+        return getAttrValue(paddr, dbrType, pbuf, nRequest);
+
     /* Check for valid request */
     if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) {
         char message[80];
@@ -1052,7 +822,8 @@
     }
 
     /* check for array */
-    if (paddr->special == SPC_DBADDR &&
+    if ((!pfl || pfl->type == dbfl_type_rec) &&
+        paddr->special == SPC_DBADDR &&
         no_elements > 1 &&
         (prset = dbGetRset(paddr)) &&
         prset->get_array_info) {
@@ -1062,15 +833,20 @@
 
     if (offset == 0 && (!nRequest || no_elements == 1)) {
         if (nRequest) *nRequest = 1;
-        if (pfl != NULL) {
+        if (!pfl || pfl->type == dbfl_type_rec) {
+            status = dbFastGetConvertRoutine[field_type][dbrType]
+                (paddr->pfield, pbuffer, paddr);
+        } else {
             DBADDR localAddr = *paddr; /* Structure copy */
-
-            localAddr.pfield = (char *)&pfl->field;
+            localAddr.field_type = pfl->field_type;
+            localAddr.field_size = pfl->field_size;
+            localAddr.no_elements = pfl->no_elements;
+            if (pfl->type == dbfl_type_val)
+                localAddr.pfield = (char *) &pfl->u.v.field;
+            else
+                localAddr.pfield = (char *)  pfl->u.r.field;
             status = dbFastGetConvertRoutine[field_type][dbrType]
                 (localAddr.pfield, pbuffer, &localAddr);
-        } else {
-            status = dbFastGetConvertRoutine[field_type][dbrType]
-                (paddr->pfield, pbuffer, paddr);
         }
     } else {
         long n;
@@ -1094,18 +870,23 @@
         /* convert database field  and place it in the buffer */
         if (n <= 0) {
             ;/*do nothing*/
-        } else if (pfl) {
+        } else if (!pfl || pfl->type == dbfl_type_rec) {
+            status = convert(paddr, pbuffer, n, no_elements, offset);
+        } else {
             DBADDR localAddr = *paddr; /* Structure copy */
-
-            localAddr.pfield = (char *)&pfl->field;
+            localAddr.field_type = pfl->field_type;
+            localAddr.field_size = pfl->field_size;
+            localAddr.no_elements = pfl->no_elements;
+            if (pfl->type == dbfl_type_val)
+                localAddr.pfield = (char *) &pfl->u.v.field;
+            else
+                localAddr.pfield = (char *)  pfl->u.r.field;
             status = convert(&localAddr, pbuffer, n, no_elements, offset);
-        } else {
-            status = convert(paddr, pbuffer, n, no_elements, offset);
         }
     }
     return status;
 }
-
+
 devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) {
     return (devSup *)ellNth(&prdes->devList, dtyp+1);
 }
@@ -1125,10 +906,9 @@
     dbCommon    *precord = paddr->precord;
     dbFldDes    *pfldDes = paddr->pfldDes;
     long        special = paddr->special;
-    DBLINK      *plink = (DBLINK *)paddr->pfield;
+    struct link *plink = (struct link *)paddr->pfield;
     const char  *pstring = (const char *)pbuffer;
     DBENTRY     dbEntry;
-    DBADDR      dbaddr;
     struct dsxt *old_dsxt = NULL;
     struct dset *new_dset = NULL;
     struct dsxt *new_dsxt = NULL;
@@ -1199,21 +979,8 @@
 
     switch (plink->type) { /* Old link type */
     case DB_LINK:
-        free(plink->value.pv_link.pvt);
-        plink->value.pv_link.pvt = 0;
-        plink->type = PV_LINK;
-        plink->value.pv_link.getCvt = 0;
-        plink->value.pv_link.pvlMask = 0;
-        plink->value.pv_link.lastGetdbrType = 0;
-        dbLockSetSplit(precord);
-        break;
-
     case CA_LINK:
-        dbCaRemoveLink(plink);
-        plink->type = PV_LINK;
-        plink->value.pv_link.getCvt = 0;
-        plink->value.pv_link.pvlMask = 0;
-        plink->value.pv_link.lastGetdbrType = 0;
+    	dbRemoveLink(plink);
         break;
 
     case CONSTANT:
@@ -1260,35 +1027,7 @@
 
     switch (plink->type) { /* New link type */
     case PV_LINK:
-        if (plink == &precord->tsel)
-            recGblTSELwasModified(plink);
-        plink->value.pv_link.precord = precord;
-
-        if (!(plink->value.pv_link.pvlMask & (pvlOptCA|pvlOptCP|pvlOptCPP)) &&
-            (dbNameToAddr(plink->value.pv_link.pvname, &dbaddr) == 0)) {
-            /* It's a DB link */
-            DBADDR      *pdbAddr;
-
-            plink->type = DB_LINK;
-            pdbAddr = dbMalloc(sizeof(struct dbAddr));
-            *pdbAddr = dbaddr; /* NB: structure copy */;
-            plink->value.pv_link.pvt = pdbAddr;
-            dbLockSetRecordLock(pdbAddr->precord);
-            dbLockSetMerge(precord, pdbAddr->precord);
-        } else { /* Make it a CA link */
-            char        *pperiod;
-
-            plink->type = CA_LINK;
-            if (pfldDes->field_type == DBF_INLINK) {
-                plink->value.pv_link.pvlMask |= pvlOptInpNative;
-            }
-            dbCaAddLink(plink);
-            if (pfldDes->field_type == DBF_FWDLINK) {
-                pperiod = strrchr(plink->value.pv_link.pvname, '.');
-                if (pperiod && strstr(pperiod, "PROC"))
-                    plink->value.pv_link.pvlMask |= pvlOptFWD;
-            }
-        }
+        dbAddLink(precord, plink, pfldDes->field_type);
         break;
 
     case CONSTANT:
@@ -1316,7 +1055,7 @@
     }
 postScanEvent:
     if (scan != precord->scan)
-        db_post_events(precord, &precord->scan, DBE_VALUE|DBE_LOG);
+        db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
 unlock:
     dbLockSetGblUnlock();
 finish:
@@ -1337,8 +1076,7 @@
         return S_db_noMod;
 
     /*check for putField disabled*/
-    if (precord->disp &&
-        (void *)(&precord->disp) != paddr->pfield)
+    if (precord->disp && paddr->pfield != &precord->disp)
         return S_db_putDisabled;
 
     if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK)
@@ -1347,7 +1085,7 @@
     dbScanLock(precord);
     status = dbPut(paddr, dbrType, pbuffer, nRequest);
     if (status == 0) {
-        if (paddr->pfield == (void *)&precord->proc ||
+        if (paddr->pfield == &precord->proc ||
             (pfldDes->process_passive &&
              precord->scan == 0 &&
              dbrType < DBR_PUT_ACKT)) {
@@ -1366,16 +1104,18 @@
     dbScanUnlock(precord);
     return status;
 }
-
-static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest,
+
+static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest,
     long no_elements, long offset)
 {
     dbCommon *precord = paddr->precord;
+    const unsigned short *ptrans = pbuffer;
 
-    if (*pbuffer == precord->ackt) return 0;
-    precord->ackt = *pbuffer;
+    if (*ptrans == precord->ackt) return 0;
+    precord->ackt = *ptrans;
     db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM);
-    if (!precord->ackt && precord->acks > precord->sevr) {
+    if (!precord->ackt &&
+        precord->acks > precord->sevr) {
         precord->acks = precord->sevr;
         db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM);
     }
@@ -1383,19 +1123,20 @@
     return 0;
 }
 
-static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest,
+static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest,
     long no_elements, long offset)
 {
     dbCommon *precord = paddr->precord;
+    const unsigned short *psev = pbuffer;
 
-    if (*pbuffer >= precord->acks) {
+    if (*psev >= precord->acks) {
         precord->acks = 0;
+        db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM);
         db_post_events(precord, NULL, DBE_ALARM);
-        db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM);
     }
     return 0;
 }
-
+
 long epicsShareAPI dbPut(DBADDR *paddr, short dbrType,
     const void *pbuffer, long nRequest)
 {
@@ -1403,17 +1144,17 @@
     short field_type  = paddr->field_type;
     long no_elements  = paddr->no_elements;
     long special      = paddr->special;
-    long offset;
     long status = 0;
     dbFldDes *pfldDes;
     int isValueField;
 
-    if (special == SPC_ATTRIBUTE) return S_db_noMod;
+    if (special == SPC_ATTRIBUTE)
+        return S_db_noMod;
 
     if (dbrType == DBR_PUT_ACKT && field_type <= DBF_DEVICE) {
-        return putAckt(paddr, (unsigned short *)pbuffer, 1, 1, 0);
+        return putAckt(paddr, pbuffer, 1, 1, 0);
     } else if (dbrType == DBR_PUT_ACKS && field_type <= DBF_DEVICE) {
-        return putAcks(paddr, (unsigned short *)pbuffer, 1, 1, 0);
+        return putAcks(paddr, pbuffer, 1, 1, 0);
     } else if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) {
         char message[80];
 
@@ -1432,6 +1173,7 @@
             paddr->pfield, paddr);
     } else {
         struct rset *prset = dbGetRset(paddr);
+        long offset = 0;
 
         if (paddr->special == SPC_DBADDR &&
             prset && prset->get_array_info) {
@@ -1439,8 +1181,6 @@
 
             status = prset->get_array_info(paddr, &dummy, &offset);
         }
-        else
-            offset = 0;
         if (no_elements < nRequest) nRequest = no_elements;
         status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
             nRequest, no_elements, offset);
@@ -1471,140 +1211,4 @@
 
     return status;
 }
-
-/* various utility routines */
-long epicsShareAPI dbGetControlLimits(
-    const struct link *plink,double *low, double *high)
-{
-    struct buffer {
-        DBRctrlDouble
-        double value;
-    } buffer;
-    DBADDR *paddr;
-    long options = DBR_CTRL_DOUBLE;
-    long number_elements = 0;
-    long status;
-
-    if(plink->type == CA_LINK) return(dbCaGetControlLimits(plink,low,high));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0);
-    if(status) return(status);
-    *low = buffer.lower_ctrl_limit;
-    *high = buffer.upper_ctrl_limit;
-    return(0);
-}
-
-long epicsShareAPI dbGetGraphicLimits(
-    const struct link *plink,double *low, double *high)
-{
-    struct buffer {
-        DBRgrDouble
-        double value;
-    } buffer;
-    DBADDR *paddr;
-    long options = DBR_GR_DOUBLE;
-    long number_elements = 0;
-    long status;
-
-    if(plink->type == CA_LINK) return(dbCaGetGraphicLimits(plink,low,high));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0);
-    if(status) return(status);
-    *low = buffer.lower_disp_limit;
-    *high = buffer.upper_disp_limit;
-    return(0);
-}
-
-long epicsShareAPI dbGetAlarmLimits(const struct link *plink,
-	double *lolo, double *low, double *high, double *hihi)
-{
-    struct buffer {
-        DBRalDouble
-        double value;
-    } buffer;
-    DBADDR *paddr;
-    long options = DBR_AL_DOUBLE;
-    long number_elements = 0;
-    long status;
-
-    if(plink->type == CA_LINK)
-        return(dbCaGetAlarmLimits(plink,lolo,low,high,hihi));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0);
-    if(status) return(status);
-    *lolo = buffer.lower_alarm_limit;
-    *low = buffer.lower_warning_limit;
-    *high = buffer.upper_warning_limit;
-    *hihi = buffer.upper_alarm_limit;
-    return(0);
-}
-
-long epicsShareAPI dbGetPrecision(const struct link *plink,short *precision)
-{
-    struct buffer {
-        DBRprecision
-        double value;
-    } buffer;
-    DBADDR *paddr;
-    long options = DBR_PRECISION;
-    long number_elements = 0;
-    long status;
-
-    if(plink->type == CA_LINK) return(dbCaGetPrecision(plink,precision));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0);
-    if(status) return(status);
-    *precision = buffer.precision.dp;
-    return(0);
-}
-
-long epicsShareAPI dbGetUnits(
-    const struct link *plink,char *units,int unitsSize)
-{
-    struct buffer {
-        DBRunits
-        double value;
-    } buffer;
-    DBADDR *paddr;
-    long options = DBR_UNITS;
-    long number_elements = 0;
-    long status;
-
-    if(plink->type == CA_LINK) return(dbCaGetUnits(plink,units,unitsSize));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0);
-    if(status) return(status);
-    strncpy(units,buffer.units,unitsSize);
-    return(0);
-}
-
-long epicsShareAPI dbGetAlarm(const struct link *plink,
-    epicsEnum16 *status,epicsEnum16 *severity)
-{
-    DBADDR *paddr;
-
-    if(plink->type == CA_LINK) return(dbCaGetAlarm(plink,status,severity));
-    if(plink->type !=DB_LINK) return(S_db_notFound);
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    if (status) *status = paddr->precord->stat;
-    if (severity) *severity = paddr->precord->sevr;
-    return(0);
-}
-
-long epicsShareAPI dbGetTimeStamp(const struct link *plink,epicsTimeStamp *pstamp)
-{
-    DBADDR *paddr;
-
-    if (plink->type == CA_LINK)
-        return dbCaGetTimeStamp(plink,pstamp);
-    if (plink->type != DB_LINK)
-        return S_db_notFound;
-    paddr = (DBADDR *)plink->value.pv_link.pvt;
-    *pstamp = paddr->precord->time;
-    return 0;
-}
+

=== modified file 'src/ioc/db/dbAccess.h'
--- src/ioc/db/dbAccess.h	2002-07-12 21:35:43 +0000
+++ src/ioc/db/dbAccess.h	2012-05-30 18:10:27 +0000
@@ -3,8 +3,7 @@
 *     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
+* EPICS BASE is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution. 
 \*************************************************************************/
 /* dbAccess.h	*/
@@ -22,6 +21,7 @@
 #include "dbAddr.h"
 #include "dbLock.h"
 #include "dbAccessDefs.h"
+#include "dbLink.h"
 #include "dbCa.h"
 #include "dbCommon.h"
 #include "db_field_log.h"

=== modified file 'src/ioc/db/dbAccessDefs.h'
--- src/ioc/db/dbAccessDefs.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbAccessDefs.h	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 /* dbAccessDefs.h	*/
 /* $Revision-Id$ */
@@ -20,6 +19,8 @@
 
 #include "epicsTypes.h"
 #include "epicsTime.h"
+#include "dbBase.h"
+#include "dbAddr.h"
 
 #ifdef INCLdb_accessh_epicsExportSharedSymbols
 #   define epicsExportSharedSymbols
@@ -47,7 +48,7 @@
 #define DBR_CTRL_DOUBLE 0x00000100
 #define DBR_AL_LONG     0x00000200
 #define DBR_AL_DOUBLE   0x00000400
-
+
 /**********************************************************************
  * The next page contains macros for defining requests.
  * As an example the following defines a buffer to accept an array
@@ -69,7 +70,7 @@
  * options = DBR_STATUS|DBR_TIME;
  * number_elements = 10;
  * rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements);
- *	
+ *
  * When dbGetField returns:
  *	rtnval is error status (0 means success)
  *	options has a bit set for each option that was accepted
@@ -97,7 +98,7 @@
  * MYBUFFER *pbuf1;
  * MYBUFFER buf;
  *************************************************************************/
-
+
 /* Macros for defining each option */
 #define DBRstatus \
 	epicsUInt16	status;		/* alarm status */\
@@ -143,7 +144,7 @@
         epicsFloat64    upper_warning_limit;\
         epicsFloat64    lower_warning_limit;\
         epicsFloat64    lower_alarm_limit;
-
+
 /*  structures for each option type             */
 struct dbr_status       {DBRstatus};
 struct dbr_units        {DBRunits};
@@ -198,25 +199,6 @@
 #define S_db_cntSpwn    (M_dbAccess|63) /*Cannot spawn dbContTask*/
 #define S_db_cntCont    (M_dbAccess|65) /*Cannot resume dbContTask*/
 #define S_db_noMemory   (M_dbAccess|66) /*unable to allocate data structure from pool*/
-
-/* Global Database Access Routines*/
-#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \
-    ( ( ( (PLNK)->type == CONSTANT ) && \
-        ( (NREQUEST) == 0) &&\
-        ( (OPTIONS) == 0) ) \
-      ? 0 \
-      : dbGetLinkValue((PLNK),(DBRTYPE), \
-        (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) )
-#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \
-    ( ( (PLNK)->type == CONSTANT) \
-      ? 0 \
-      : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) )
-#define dbGetPdbAddrFromLink(PLNK) \
-    ( ( (PLNK)->type != DB_LINK ) \
-      ? 0 \
-      : ( ( (struct dbAddr *)( (PLNK)->value.pv_link.pvt) ) ) )
-#define dbGetSevr(PLINK,PSEVERITY) \
-    dbGetAlarm((PLINK),NULL,(PSEVERITY))
 
 epicsShareFunc long epicsShareAPI dbPutSpecial(struct dbAddr *paddr,int pass);
 epicsShareFunc struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr);
@@ -224,51 +206,24 @@
     const char *recordTypename,const char *name,const char*value);
 epicsShareFunc int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes);
 epicsShareFunc int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr);
-epicsShareFunc long epicsShareAPI dbGetNelements(
-    const struct link *plink,long *nelements);
-epicsShareFunc int epicsShareAPI dbIsLinkConnected(const struct link *plink);
-epicsShareFunc int epicsShareAPI dbGetLinkDBFtype(const struct link *plink);
-epicsShareFunc long epicsShareAPI dbScanLink(
-    struct dbCommon *pfrom, struct dbCommon *pto);
 epicsShareFunc long epicsShareAPI dbScanPassive(
     struct dbCommon *pfrom,struct dbCommon *pto);
-epicsShareFunc void epicsShareAPI dbScanFwdLink(struct link *plink);
 epicsShareFunc long epicsShareAPI dbProcess(struct dbCommon *precord);
 epicsShareFunc long epicsShareAPI dbNameToAddr(
     const char *pname,struct dbAddr *);
 epicsShareFunc devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
 epicsShareFunc devSup* epicsShareAPI dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
-epicsShareFunc long epicsShareAPI dbGetLinkValue(
-    struct link *,short dbrType,void *pbuffer,long *options,long *nRequest);
 epicsShareFunc long epicsShareAPI dbGetField(
     struct dbAddr *,short dbrType,void *pbuffer,long *options,
     long *nRequest,void *pfl);
 epicsShareFunc long epicsShareAPI dbGet(
     struct dbAddr *,short dbrType,void *pbuffer,long *options,
     long *nRequest,void *pfl);
-epicsShareFunc long epicsShareAPI dbPutLinkValue(
-    struct link *,short dbrType,const void *pbuffer,long nRequest);
 epicsShareFunc long epicsShareAPI dbPutField(
     struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
 epicsShareFunc long epicsShareAPI dbPut(
     struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
 
-/* various utility routines */
-epicsShareFunc long epicsShareAPI dbGetControlLimits(
-    const struct link *plink,double *low, double *high);
-epicsShareFunc long epicsShareAPI dbGetGraphicLimits(
-    const struct link *plink,double *low, double *high);
-epicsShareFunc long epicsShareAPI dbGetAlarmLimits(
-    const struct link *plink,double *lolo, double *low, double *high, double *hihi);
-epicsShareFunc long epicsShareAPI dbGetPrecision(
-    const struct link *plink,short *precision);
-epicsShareFunc long epicsShareAPI dbGetUnits(
-    const struct link *plink,char *units,int unitsSize);
-epicsShareFunc long epicsShareAPI dbGetAlarm(
-    const struct link *plink, epicsEnum16 *status,epicsEnum16 *severity);
-epicsShareFunc long epicsShareAPI dbGetTimeStamp(
-    const struct link *plink,epicsTimeStamp *pstamp);
-
 typedef void(*SPC_ASCALLBACK)(struct dbCommon *);
 /*dbSpcAsRegisterCallback called by access security */
 epicsShareFunc void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func);

=== modified file 'src/ioc/db/dbAddr.h'
--- src/ioc/db/dbAddr.h	2008-08-15 18:58:18 +0000
+++ src/ioc/db/dbAddr.h	2012-05-30 18:10:27 +0000
@@ -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 dbAddrh
@@ -27,7 +27,4 @@
 
 typedef dbAddr DBADDR;
 
-unsigned dbNameOfPV (const dbAddr * paddr, char * pBuf, unsigned bufLen);
-unsigned dbNameSizeOfPV (const dbAddr * paddr);
-
 #endif /* dbAddrh */

=== modified file 'src/ioc/db/dbBkpt.c'
--- src/ioc/db/dbBkpt.c	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbBkpt.c	2012-05-30 18:10:27 +0000
@@ -65,6 +65,7 @@
 #include "dbAddr.h"
 #include "dbAccessDefs.h"
 #include "dbScan.h"
+#include "dbLink.h"
 #include "dbLock.h"
 #include "recGbl.h"
 #include "dbTest.h"

=== modified file 'src/ioc/db/dbCAC.h'
--- src/ioc/db/dbCAC.h	2011-06-01 22:22:12 +0000
+++ src/ioc/db/dbCAC.h	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 /*
  *  $Revision-Id$
@@ -49,7 +49,7 @@
 #include "db_access.h"
 #include "dbNotify.h"
 #include "dbEvent.h"
-#include "dbAddr.h"
+#include "dbChannel.h"
 #include "dbLock.h"
 #include "dbCommon.h"
 #include "db_convert.h"
@@ -75,25 +75,25 @@
     virtual ~dbBaseIO() {}
 };
 
-extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr,
+extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel *dbch,
     int eventsRemaining, struct db_field_log *pfl );
 
-class dbSubscriptionIO : 
-    public tsDLNode < dbSubscriptionIO >, 
+class dbSubscriptionIO :
+    public tsDLNode < dbSubscriptionIO >,
     public dbBaseIO {
 public:
-    dbSubscriptionIO ( 
+    dbSubscriptionIO (
         epicsGuard < epicsMutex > &, epicsMutex &,
-        dbContext &, dbChannelIO &, struct dbAddr &, cacStateNotify &, 
+        dbContext &, dbChannelIO &, struct dbChannel *, cacStateNotify &,
         unsigned type, unsigned long count, unsigned mask, dbEventCtx );
     void destructor ( epicsGuard < epicsMutex > & );
     void unsubscribe ( epicsGuard < epicsMutex > & );
     void channelDeleteException ( epicsGuard < epicsMutex > & );
     void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
     void show ( unsigned level ) const;
-    void * operator new ( size_t size, 
+    void * operator new ( size_t size,
         tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & );
-    epicsPlacementDeleteOperator (( void *, 
+    epicsPlacementDeleteOperator (( void *,
         tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ))
 private:
     epicsMutex & mutex;
@@ -104,8 +104,8 @@
     unsigned type;
     unsigned id;
     dbSubscriptionIO * isSubscription ();
-    friend void dbSubscriptionEventCallback ( 
-        void * pPrivate, struct dbAddr * paddr,
+    friend void dbSubscriptionEventCallback (
+        void * pPrivate, struct dbChannel * dbch,
         int eventsRemaining, struct db_field_log * pfl );
     dbSubscriptionIO ( const dbSubscriptionIO & );
     dbSubscriptionIO & operator = ( const dbSubscriptionIO & );
@@ -149,8 +149,8 @@
 class dbContextReadNotifyCache  {
 public:
     dbContextReadNotifyCache ( epicsMutex & );
-    void callReadNotify ( epicsGuard < epicsMutex > &, 
-        struct dbAddr & addr, unsigned type, unsigned long count, 
+    void callReadNotify ( epicsGuard < epicsMutex > &,
+        struct dbChannel * dbch, unsigned type, unsigned long count,
             cacReadNotify & notify );
     void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
 private:
@@ -162,29 +162,29 @@
 
 class dbContext : public cacContext {
 public:
-    dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, 
+    dbContext ( epicsMutex & cbMutex, epicsMutex & mutex,
         cacContextNotify & notify );
     virtual ~dbContext ();
     void destroyChannel ( epicsGuard < epicsMutex > &, dbChannelIO & );
-    void callReadNotify ( epicsGuard < epicsMutex > &, 
-            struct dbAddr & addr, unsigned type, unsigned long count, 
+    void callReadNotify ( epicsGuard < epicsMutex > &,
+            struct dbChannel * dbch, unsigned type, unsigned long count,
             cacReadNotify & notify );
-    void callStateNotify ( struct dbAddr &addr, unsigned type, unsigned long count, 
+    void callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count,
             const struct db_field_log * pfl, cacStateNotify & notify );
-    void subscribe ( 
+    void subscribe (
             epicsGuard < epicsMutex > &,
-            struct dbAddr & addr, dbChannelIO & chan,
-            unsigned type, unsigned long count, unsigned mask, 
+            struct dbChannel * dbch, dbChannelIO & chan,
+            unsigned type, unsigned long count, unsigned mask,
             cacStateNotify & notify, cacChannel::ioid * pId );
-    void initiatePutNotify ( 
-            epicsGuard < epicsMutex > &, dbChannelIO &, struct dbAddr &, 
-            unsigned type, unsigned long count, const void * pValue, 
-            cacWriteNotify & notify, cacChannel::ioid * pId ); 
+    void initiatePutNotify (
+            epicsGuard < epicsMutex > &, dbChannelIO &, struct dbChannel *,
+            unsigned type, unsigned long count, const void * pValue,
+            cacWriteNotify & notify, cacChannel::ioid * pId );
     void show ( unsigned level ) const;
     void showAllIO ( const dbChannelIO & chan, unsigned level ) const;
-    void destroyAllIO ( 
+    void destroyAllIO (
         epicsGuard < epicsMutex > &, dbChannelIO & chan );
-    void ioCancel ( epicsGuard < epicsMutex > &, 
+    void ioCancel ( epicsGuard < epicsMutex > &,
         dbChannelIO & chan, const cacChannel::ioid &id );
     void ioShow ( epicsGuard < epicsMutex > &,
         const cacChannel::ioid & id, unsigned level ) const;
@@ -202,11 +202,11 @@
     epics_auto_ptr < cacContext > pNetContext;
     char * pStateNotifyCache;
 
-    cacChannel & createChannel ( 
+    cacChannel & createChannel (
         epicsGuard < epicsMutex > &,
-        const char * pChannelName, cacChannelNotify &, 
+        const char * pChannelName, cacChannelNotify &,
         cacChannel::priLev );
-    void flush ( 
+    void flush (
         epicsGuard < epicsMutex > & );
     unsigned circuitCount (
         epicsGuard < epicsMutex > & ) const;
@@ -214,7 +214,7 @@
         epicsGuard < epicsMutex > & ) const;
     unsigned beaconAnomaliesSinceProgramStart (
         epicsGuard < epicsMutex > & ) const;
-    void show ( 
+    void show (
         epicsGuard < epicsMutex > &, unsigned level ) const;
 
     dbContext ( const dbContext & );
@@ -226,17 +226,17 @@
 {
 }
 
-inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () 
+inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO ()
 {
     assert ( ! this->pBlocker );
 }
 
-inline void dbContext::callReadNotify ( 
-    epicsGuard < epicsMutex > & guard, struct dbAddr &addr, 
+inline void dbContext::callReadNotify (
+    epicsGuard < epicsMutex > & guard, struct dbChannel * dbch,
     unsigned type, unsigned long count, cacReadNotify & notifyIn )
 {
     guard.assertIdenticalMutex ( this-> mutex );
-    this->readNotifyCache.callReadNotify ( guard, addr, type, count, notifyIn );
+    this->readNotifyCache.callReadNotify ( guard, dbch, type, count, notifyIn );
 }
 
 #endif // dbCACh

=== modified file 'src/ioc/db/dbCa.c'
--- src/ioc/db/dbCa.c	2012-05-04 18:38:59 +0000
+++ src/ioc/db/dbCa.c	2012-05-30 18:10:27 +0000
@@ -411,6 +411,13 @@
     return pca->isConnected;
 }
 
+void dbCaScanFwdLink(struct link *plink) {
+    short fwdLinkValue = 1;
+
+    if (plink->value.pv_link.pvlMask & pvlOptFWD)
+        dbCaPutLink(plink, DBR_SHORT, &fwdLinkValue, 1);
+}
+
 #define pcaGetCheck \
     assert(plink); \
     if (plink->type != CA_LINK) return -1; \
@@ -639,7 +646,7 @@
 static void eventCallback(struct event_handler_args arg)
 {
     caLink *pca = (caLink *)arg.usr;
-    DBLINK *plink;
+    struct link *plink;
     size_t size;
     dbCommon *precord = 0;
     struct dbr_time_double *pdbr_time_double;

=== modified file 'src/ioc/db/dbCa.h'
--- src/ioc/db/dbCa.h	2009-04-29 18:24:25 +0000
+++ src/ioc/db/dbCa.h	2012-05-30 18:10:27 +0000
@@ -40,6 +40,7 @@
 #define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \
     dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0)
 epicsShareFunc int dbCaIsLinkConnected(const struct link *plink);
+epicsShareFunc void dbCaScanFwdLink(struct link *plink);
 
 /* The following are available after the link is connected*/
 epicsShareFunc long dbCaGetNelements(const struct link *plink,

=== added file 'src/ioc/db/dbChannel.c'
--- src/ioc/db/dbChannel.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbChannel.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,864 @@
+/*************************************************************************\
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Andrew Johnson <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cantProceed.h"
+#include "dbChannel.h"
+#include "dbCommon.h"
+#include "dbBase.h"
+#include "dbEvent.h"
+#include "link.h"
+#include <freeList.h>
+#include "dbAccessDefs.h"
+#include "dbLock.h"
+#include "dbStaticLib.h"
+#include "epicsAssert.h"
+#include "errlog.h"
+#include "gpHash.h"
+#include "recSup.h"
+#include "special.h"
+#include "yajl_parse.h"
+
+/* The following is defined in db_convert.h */
+extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1];
+
+typedef struct parseContext {
+    dbChannel *chan;
+    chFilter *filter;
+    int depth;
+} parseContext;
+
+#define CALLIF(rtn) !rtn ? parse_stop : rtn
+
+static void *dbchStringFreeList;
+
+void dbChannelInit (void)
+{
+    if (!dbchStringFreeList) {
+        freeListInitPvt(&dbchStringFreeList,
+            sizeof(epicsOldString), 128);
+    }
+}
+
+static void chf_value(parseContext *parser, parse_result *presult)
+{
+    chFilter *filter = parser->filter;
+
+    if (*presult == parse_stop || parser->depth > 0)
+        return;
+
+    parser->filter = NULL;
+    if (filter->plug->fif->parse_end(filter) == parse_continue) {
+        ellAdd(&parser->chan->filters, &filter->list_node);
+    } else {
+        free(filter); // FIXME: Use free-list
+        *presult = parse_stop;
+    }
+}
+
+static int chf_null(void * ctx)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_null)(filter );
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_boolean(void * ctx, int boolVal)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_boolean)(filter , boolVal);
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_integer(void * ctx, long integerVal)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_integer)(filter , integerVal);
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_double(void * ctx, double doubleVal)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_double)(filter , doubleVal);
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_string(void * ctx, const unsigned char * stringVal,
+        unsigned int stringLen)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_string)(filter , (const char *) stringVal, stringLen);
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_start_map(void * ctx)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+
+    if (!filter) {
+        assert(parser->depth == 0);
+        return parse_continue; /* Opening '{' */
+    }
+
+    ++parser->depth;
+    return CALLIF(filter->plug->fif->parse_start_map)(filter );
+}
+
+static int chf_map_key(void * ctx, const unsigned char * key,
+        unsigned int stringLen)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    const chFilterPlugin *plug;
+    parse_result result;
+
+    if (filter) {
+        assert(parser->depth > 0);
+        return CALLIF(filter->plug->fif->parse_map_key)(filter , (const char *) key, stringLen);
+    }
+
+    assert(parser->depth == 0);
+    plug = dbFindFilter((const char *) key, stringLen);
+    if (!plug) {
+        printf("dbChannelCreate: Channel filter '%.*s' not found\n", stringLen, key);
+        return parse_stop;
+    }
+
+    /* FIXME: Use a free-list */
+    filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter");
+    filter->chan = parser->chan;
+    filter->plug = plug;
+    filter->puser = NULL;
+
+    result = plug->fif->parse_start(filter);
+    if (result == parse_continue) {
+        parser->filter = filter;
+    } else {
+        free(filter); // FIXME: Use free-list
+    }
+    return result;
+}
+
+static int chf_end_map(void * ctx)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    if (!filter) {
+        assert(parser->depth == 0);
+        return parse_continue; /* Final closing '}' */
+    }
+
+    assert(parser->depth > 0);
+    result = CALLIF(filter->plug->fif->parse_end_map)(filter );
+
+    --parser->depth;
+    chf_value(parser, &result);
+    return result;
+}
+
+static int chf_start_array(void * ctx)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+
+    assert(filter);
+    ++parser->depth;
+    return CALLIF(filter->plug->fif->parse_start_array)(filter );
+}
+
+static int chf_end_array(void * ctx)
+{
+    parseContext *parser = (parseContext *) ctx;
+    chFilter *filter = parser->filter;
+    parse_result result;
+
+    assert(filter);
+    result = CALLIF(filter->plug->fif->parse_end_array)(filter );
+    --parser->depth;
+    chf_value(parser, &result);
+    return result;
+}
+
+static const yajl_callbacks chf_callbacks =
+    { chf_null, chf_boolean, chf_integer, chf_double, NULL, chf_string,
+      chf_start_map, chf_map_key, chf_end_map, chf_start_array, chf_end_array };
+
+static const yajl_parser_config chf_config =
+    { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */
+
+static void * chf_malloc(void *ctx, unsigned int sz)
+{
+    return malloc(sz); /* FIXME: free-list */
+}
+
+static void * chf_realloc(void *ctx, void *ptr, unsigned int sz)
+{
+    return realloc(ptr, sz); /* FIXME: free-list */
+}
+
+static void chf_free(void *ctx, void *ptr)
+{
+    return free(ptr); /* FIXME: free-list */
+}
+
+static const yajl_alloc_funcs chf_alloc =
+    { chf_malloc, chf_realloc, chf_free };
+
+static long chf_parse(dbChannel *chan, const char **pjson)
+{
+    parseContext parser =
+        { chan, NULL, 0 };
+    yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, &chf_alloc, &parser);
+    const char *json = *pjson;
+    size_t jlen = strlen(json);
+    yajl_status ys;
+    long status;
+
+    if (!yh)
+        return S_db_noMemory;
+
+    ys = yajl_parse(yh, (const unsigned char *) json, jlen);
+    if (ys == yajl_status_insufficient_data)
+        ys = yajl_parse_complete(yh);
+
+    switch (ys) {
+    case yajl_status_ok:
+        status = 0;
+        *pjson += yajl_get_bytes_consumed(yh);
+        break;
+
+    case yajl_status_error: {
+        unsigned char *err;
+
+        err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
+        printf("dbChannelCreate: %s\n", err);
+        yajl_free_error(yh, err);
+    } /* fall through */
+    default:
+        status = S_db_notFound;
+    }
+
+    if (parser.filter) {
+        assert(status);
+        parser.filter->plug->fif->parse_abort(parser.filter);
+        free(parser.filter); /* FIXME: free-list */
+    }
+    yajl_free(yh);
+    return status;
+}
+
+static long pvNameLookup(DBENTRY *pdbe, const char **ppname)
+{
+    long status;
+
+    dbInitEntry(pdbbase, pdbe);
+
+    status = dbFindRecordPart(pdbe, ppname);
+    if (status)
+        return status;
+
+    if (**ppname == '.')
+        ++*ppname;
+
+    status = dbFindFieldPart(pdbe, ppname);
+    if (status == S_dbLib_fieldNotFound)
+        status = dbGetAttributePart(pdbe, ppname);
+
+    return status;
+}
+
+long dbChannelTest(const char *name)
+{
+    DBENTRY dbEntry;
+    long status;
+
+    if (!name || !*name || !pdbbase)
+        return S_db_notFound;
+
+    status = pvNameLookup(&dbEntry, &name);
+
+    dbFinishEntry(&dbEntry);
+    return status;
+}
+
+#define TRY(Func, Arg) \
+if (Func) { \
+    result = Func Arg; \
+    if (result != parse_continue) goto failure; \
+}
+
+static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) {
+    epicsInt32 start = 0;
+    epicsInt32 end = -1;
+    epicsInt32 incr = 1;
+    epicsInt32 l;
+    char *pnext;
+    short exist;
+    chFilter *filter;
+    const chFilterPlugin *plug;
+    parse_result result;
+    long status = 0;
+
+    /* If no number is present, strtol() returns 0 and sets pnext=pname,
+       else pnext points to the first char after the number */
+    pname++;
+    l = strtol(pname, &pnext, 0);
+    exist = pnext - pname;
+    if (exist) start = l;
+    pname = pnext;
+    if (*pname == ']' && exist) {
+        end = start;
+        goto insertplug;
+    }
+    if (*pname != ':') {
+        status = S_dbLib_fieldNotFound;
+        goto finish;
+    }
+    pname++;
+    l = strtol(pname, &pnext, 0);
+    exist = pnext - pname;
+    pname = pnext;
+    if (*pname == ']') {
+        if (exist) end = l;
+        goto insertplug;
+    }
+    if (exist) incr = l;
+    if (*pname != ':') {
+        status = S_dbLib_fieldNotFound;
+        goto finish;
+    }
+    pname++;
+    l = strtol(pname, &pnext, 0);
+    exist = pnext - pname;
+    if (exist) end = l;
+    pname = pnext;
+    if (*pname != ']') {
+        status = S_dbLib_fieldNotFound;
+        goto finish;
+    }
+
+    insertplug:
+    pname++;
+    *ppnext = pname;
+
+    plug = dbFindFilter("arr", 3);
+    if (!plug) {
+        status = S_dbLib_fieldNotFound;
+        goto finish;
+    }
+
+    /* FIXME: Use a free-list */
+    filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating 'arr' dbChannel filter");
+    filter->chan = chan;
+    filter->plug = plug;
+    filter->puser = NULL;
+
+    TRY(filter->plug->fif->parse_start, (filter));
+    TRY(filter->plug->fif->parse_start_map, (filter));
+    if (start != 0) {
+        TRY(filter->plug->fif->parse_map_key, (filter, "s", 1));
+        TRY(filter->plug->fif->parse_integer, (filter, start));
+    }
+    if (incr != 1) {
+        TRY(filter->plug->fif->parse_map_key, (filter, "i", 1));
+        TRY(filter->plug->fif->parse_integer, (filter, incr));
+    }
+    if (end != -1) {
+        TRY(filter->plug->fif->parse_map_key, (filter, "e", 1));
+        TRY(filter->plug->fif->parse_integer, (filter, end));
+    }
+    TRY(filter->plug->fif->parse_end_map, (filter));
+    TRY(filter->plug->fif->parse_end, (filter));
+
+    ellAdd(&chan->filters, &filter->list_node);
+    return 0;
+
+    failure:
+    free(filter); // FIXME: Use free-list
+    status = S_dbLib_fieldNotFound;
+
+    finish:
+    return status;
+}
+
+/* Stolen from dbAccess.c: */
+static short mapDBFToDBR[DBF_NTYPES] =
+    {
+    /* DBF_STRING   => */DBR_STRING,
+    /* DBF_CHAR     => */DBR_CHAR,
+    /* DBF_UCHAR    => */DBR_UCHAR,
+    /* DBF_SHORT    => */DBR_SHORT,
+    /* DBF_USHORT   => */DBR_USHORT,
+    /* DBF_LONG     => */DBR_LONG,
+    /* DBF_ULONG    => */DBR_ULONG,
+    /* DBF_FLOAT    => */DBR_FLOAT,
+    /* DBF_DOUBLE   => */DBR_DOUBLE,
+    /* DBF_ENUM,    => */DBR_ENUM,
+    /* DBF_MENU,    => */DBR_ENUM,
+    /* DBF_DEVICE   => */DBR_ENUM,
+    /* DBF_INLINK   => */DBR_STRING,
+    /* DBF_OUTLINK  => */DBR_STRING,
+    /* DBF_FWDLINK  => */DBR_STRING,
+    /* DBF_NOACCESS => */DBR_NOACCESS };
+
+dbChannel * dbChannelCreate(const char *name)
+{
+    const char *pname = name;
+    DBENTRY dbEntry;
+    dbChannel *chan = NULL;
+    dbAddr *paddr;
+    dbFldDes *pflddes;
+    long status;
+    short dbfType;
+
+    if (!name || !*name || !pdbbase)
+        return NULL;
+
+    status = pvNameLookup(&dbEntry, &pname);
+    if (status)
+        goto finish;
+
+    /* FIXME: Use free-list */
+    chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate");
+    chan->name = strdup(name);  /* FIXME: free-list */
+    ellInit(&chan->filters);
+    ellInit(&chan->pre_chain);
+    ellInit(&chan->post_chain);
+
+    paddr = &chan->addr;
+    pflddes = dbEntry.pflddes;
+    dbfType = pflddes->field_type;
+
+    paddr->precord = dbEntry.precnode->precord;
+    paddr->pfield = dbEntry.pfield;
+    paddr->pfldDes = pflddes;
+    paddr->no_elements = 1;
+    paddr->field_type = dbfType;
+    paddr->field_size = pflddes->size;
+    paddr->special = pflddes->special;
+    paddr->dbr_field_type = mapDBFToDBR[dbfType];
+
+    /* Handle field modifiers */
+    if (*pname) {
+        if (*pname == '$') {
+            /* Some field types can be accessed as char arrays */
+            if (dbfType == DBF_STRING) {
+                paddr->no_elements = pflddes->size;
+                paddr->field_type = DBF_CHAR;
+                paddr->field_size = 1;
+                paddr->dbr_field_type = DBR_CHAR;
+            } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
+                /* Clients see a char array, but keep original dbfType */
+                paddr->no_elements = PVNAME_STRINGSZ + 12;
+                paddr->field_size = 1;
+                paddr->dbr_field_type = DBR_CHAR;
+            } else {
+                status = S_dbLib_fieldNotFound;
+                goto finish;
+            }
+            pname++;
+        }
+
+        if (*pname == '[') {
+            status = parseArrayRange(chan, pname, &pname);
+            if (status) goto finish;
+        }
+
+        /* JSON may follow */
+        if (*pname == '{') {
+            status = chf_parse(chan, &pname);
+            if (status) goto finish;
+        }
+
+        /* Make sure there's nothing else */
+        if (*pname) {
+            status = S_dbLib_fieldNotFound;
+            goto finish;
+        }
+    }
+
+    if (paddr->special == SPC_DBADDR) {
+        struct rset *prset = dbGetRset(paddr);
+
+        /* Let record type modify the dbAddr */
+        if (prset && prset->cvt_dbaddr) {
+            status = prset->cvt_dbaddr(paddr);
+            if (status) goto finish;
+        }
+    }
+
+finish:
+    if (status && chan) {
+        dbChannelDelete(chan);
+        chan = NULL;
+    }
+    dbFinishEntry(&dbEntry);
+    return chan;
+}
+
+db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) {
+    chFilter *filter;
+    ELLNODE *node;
+    db_field_log *pLog = pLogIn;
+
+    for (node = ellFirst(&chan->pre_chain); node && pLog; node = ellNext(node)) {
+        filter = CONTAINER(node, chFilter, pre_node);
+        pLog = filter->pre_func(filter->pre_arg, chan, pLog);
+    }
+    return pLog;
+}
+
+db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) {
+    chFilter *filter;
+    ELLNODE *node;
+    db_field_log *pLog = pLogIn;
+
+    for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) {
+        filter = CONTAINER(node, chFilter, post_node);
+        pLog = filter->post_func(filter->post_arg, chan, pLog);
+    }
+    return pLog;
+}
+
+long dbChannelOpen(dbChannel *chan)
+{
+    chFilter *filter;
+    chPostEventFunc *func;
+    void *arg;
+    long status;
+    ELLNODE *node;
+
+    for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
+        filter = CONTAINER(node, chFilter, list_node);
+         /* Call channel_open */
+        status = 0;
+        if (filter->plug->fif->channel_open)
+            status = filter->plug->fif->channel_open(filter);
+        if (status) return status;
+    }
+
+    /* Set up type probe */
+    db_field_log probe;
+    db_field_log p;
+    probe.field_type  = dbChannelFieldType(chan);
+    probe.no_elements = dbChannelElements(chan);
+    probe.field_size  = dbChannelFieldSize(chan);
+    p = probe;
+
+    /*
+     * Build up the pre- and post-event-queue filter chains
+     * Separate loops because the probe must reach the filters in the right order.
+     */
+    for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
+        filter = CONTAINER(node, chFilter, list_node);
+        func = NULL;
+        arg = NULL;
+        if (filter->plug->fif->channel_register_pre) {
+            filter->plug->fif->channel_register_pre(filter, &func, &arg, &p);
+            if (func) {
+                ellAdd(&chan->pre_chain, &filter->pre_node);
+                filter->pre_func = func;
+                filter->pre_arg  = arg;
+                probe = p;
+            }
+        }
+    }
+    for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
+        filter = CONTAINER(node, chFilter, list_node);
+        func = NULL;
+        arg = NULL;
+        if (filter->plug->fif->channel_register_post) {
+            filter->plug->fif->channel_register_post(filter, &func, &arg, &p);
+            if (func) {
+                ellAdd(&chan->post_chain, &filter->post_node);
+                filter->post_func = func;
+                filter->post_arg  = arg;
+                probe = p;
+            }
+        }
+    }
+
+    /* Save probe results */
+    chan->final_no_elements  = probe.no_elements;
+    chan->final_field_size   = probe.field_size;
+    chan->final_type         = probe.field_type;
+    chan->dbr_final_type     = dbDBRnewToDBRold[mapDBFToDBR[probe.field_type]];
+
+    return 0;
+}
+
+/* FIXME: For performance we should make these one-liners into macros,
+ * or try to make them inline if all our compilers can do that.
+ */
+const char * dbChannelName(dbChannel *chan)
+{
+    return chan->name;
+}
+
+struct dbCommon * dbChannelRecord(dbChannel *chan)
+{
+    return chan->addr.precord;
+}
+
+struct dbFldDes * dbChannelFldDes(dbChannel *chan)
+{
+    return chan->addr.pfldDes;
+}
+
+long dbChannelElements(dbChannel *chan)
+{
+    return chan->addr.no_elements;
+}
+
+short dbChannelFieldType(dbChannel *chan)
+{
+    return chan->addr.field_type;
+}
+
+short dbChannelExportType(dbChannel *chan)
+{
+    return chan->addr.dbr_field_type;
+}
+
+short dbChannelFieldSize(dbChannel *chan)
+{
+    return chan->addr.field_size;
+}
+
+long dbChannelFinalElements(dbChannel *chan)
+{
+    return chan->final_no_elements;
+}
+
+short dbChannelFinalFieldType(dbChannel *chan)
+{
+    return chan->final_type;
+}
+
+short dbChannelFinalExportType(dbChannel *chan)
+{
+    return chan->dbr_final_type;
+}
+
+short dbChannelFinalFieldSize(dbChannel *chan)
+{
+    return chan->final_field_size;
+}
+
+short dbChannelSpecial(dbChannel *chan)
+{
+    return chan->addr.special;
+}
+
+void * dbChannelField(dbChannel *chan)
+{
+    /* Channel filters do not get to interpose here since there are many
+     * places where the field pointer is compared with the address of a
+     * specific record field, so they can't modify the pointer value.
+     */
+    return chan->addr.pfield;
+}
+
+/* Only use dbChannelGet() if the record is already locked. */
+long dbChannelGet(dbChannel *chan, short type, void *pbuffer,
+        long *options, long *nRequest, void *pfl)
+{
+    /* FIXME: Vector through chan->get() ? */
+    return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl);
+}
+
+long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
+        long *options, long *nRequest, void *pfl)
+{
+    dbCommon *precord = chan->addr.precord;
+    long status = 0;
+
+    dbScanLock(precord);
+    status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl);
+    dbScanUnlock(precord);
+    return status;
+}
+
+/* Only use dbChannelPut() if the record is already locked.
+ * This routine doesn't work on link fields, ignores DISP, and
+ * doesn't trigger record processing on PROC or pp(TRUE).
+ */
+long dbChannelPut(dbChannel *chan, short type, const void *pbuffer,
+        long nRequest)
+{
+    /* FIXME: Vector through chan->put() ? */
+    return dbPut(&chan->addr, type, pbuffer, nRequest);
+}
+
+long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer,
+        long nRequest)
+{
+    /* FIXME: Vector through chan->putField() ? */
+    return dbPutField(&chan->addr, type, pbuffer, nRequest);
+}
+
+void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
+{
+    long elems = chan->addr.no_elements;
+    long felems = chan->final_no_elements;
+    int count = ellCount(&chan->filters);
+    int pre   = ellCount(&chan->pre_chain);
+    int post  = ellCount(&chan->post_chain);
+
+    printf("%*schannel name: %s\n", indent, "", chan->name);
+    /* FIXME: show field_type as text */
+    printf("%*s  field_type=%d (%dB), %ld element%s, %d filter%s", indent, "",
+           chan->addr.field_type, chan->addr.field_size, elems, elems == 1 ? "" : "s",
+           count, count == 1 ? "" : "s");
+    if (count)
+        printf(" (%d pre eventq, %d post eventq)\n", pre, post);
+    else
+        printf("\n");
+    if (level > 0)
+        dbChannelFilterShow(chan, level - 1, indent + 2);
+    if (count) {
+        /* FIXME: show field_type as text */
+        printf("%*s  final field_type=%d (%dB), %ld element%s\n", indent, "",
+               chan->final_type, chan->final_field_size, felems, felems == 1 ? "" : "s");
+    }
+}
+
+void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent)
+{
+    chFilter *filter = (chFilter *) ellFirst(&chan->filters);
+    while (filter) {
+        filter->plug->fif->channel_report(filter, level, indent);
+        filter = (chFilter *) ellNext(&filter->list_node);
+    }
+}
+
+void dbChannelDelete(dbChannel *chan)
+{
+    chFilter *filter;
+
+    /* Close filters in reverse order */
+    while ((filter = (chFilter *) ellPop(&chan->filters))) {
+        filter->plug->fif->channel_close(filter);
+        free(filter);
+    }
+    free((char *) chan->name);   // FIXME: Use free-list
+    free(chan); // FIXME: Use free-list
+}
+
+static void freeArray(db_field_log *pfl) {
+    if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
+        freeListFree(dbchStringFreeList, pfl->u.r.field);
+    } else {
+        free(pfl->u.r.field);
+    }
+}
+
+void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
+{
+    void *p;
+
+    if (!pfl->type == dbfl_type_rec) return;
+
+    struct dbCommon *prec = dbChannelRecord(chan);
+    pfl->type = dbfl_type_ref;
+    pfl->stat = prec->stat;
+    pfl->sevr = prec->sevr;
+    pfl->time = prec->time;
+    pfl->field_type  = chan->addr.field_type;
+    pfl->no_elements = chan->addr.no_elements;
+    pfl->field_size  = chan->addr.field_size;
+    pfl->u.r.dtor = freeArray;
+    pfl->u.r.pvt = pvt;
+    if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
+        p = freeListCalloc(dbchStringFreeList);
+    } else {
+        p = calloc(pfl->no_elements, pfl->field_size);
+    }
+    if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
+    pfl->u.r.field = p;
+}
+
+/* FIXME: Do these belong in a different file? */
+
+void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
+{
+    GPHENTRY *pgph;
+    chFilterPlugin *pfilt;
+
+    if (!pdbbase) {
+        printf("dbRegisterFilter: pdbbase not set!\n");
+        return;
+    }
+
+    pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList);
+    if (pgph)
+        return;
+
+    pfilt = dbCalloc(1, sizeof(chFilterPlugin));
+    pfilt->name = strdup(name);
+    pfilt->fif = fif;
+    pfilt->puser = puser;
+
+    ellAdd(&pdbbase->filterList, &pfilt->node);
+    pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList);
+    if (!pgph) {
+        free((void *) pfilt->name);
+        free(pfilt);
+        printf("dbRegisterFilter: gphAdd failed\n");
+        return;
+    }
+    pgph->userPvt = pfilt;
+}
+
+const chFilterPlugin * dbFindFilter(const char *name, size_t len)
+{
+    GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len,
+            &pdbbase->filterList);
+
+    if (!pgph)
+        return NULL;
+    return (chFilterPlugin *) pgph->userPvt;
+}

=== added file 'src/ioc/db/dbChannel.h'
--- src/ioc/db/dbChannel.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbChannel.h	2012-05-30 18:10:27 +0000
@@ -0,0 +1,181 @@
+/*************************************************************************\
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Andrew Johnson <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#ifndef INC_dbChannel_H
+#define INC_dbChannel_H
+
+#include "dbDefs.h"
+#include "dbAddr.h"
+#include "ellLib.h"
+#include "epicsTypes.h"
+#include "errMdef.h"
+#include "shareLib.h"
+#include "db_field_log.h"
+#include "dbEvent.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * event subscription
+ */
+typedef struct evSubscrip {
+    ELLNODE                 node;
+    struct dbChannel        *chan;
+    EVENTFUNC               *user_sub;
+    void                    *user_arg;
+    struct event_que        *ev_que;
+    db_field_log            **pLastLog;
+    unsigned long           npend;  /* n times this event is on the queue */
+    unsigned long           nreplace;  /* n times replacing event on the queue */
+    unsigned char           select;
+    char                    useValque;
+    char                    callBackInProgress;
+    char                    enabled;
+} evSubscrip;
+
+typedef struct chFilter chFilter;
+
+/* A dbChannel points to a record field, and can have multiple filters */
+typedef struct dbChannel {
+    const char *name;
+    dbAddr addr;              /* address structure for record/field */
+    long  final_no_elements;  /* final number of elements (arrays) */
+    short final_field_size;   /* final size of element */
+    short final_type;         /* final type of database field */
+    short dbr_final_type;     /* final field type as seen by database request */
+    ELLLIST filters;          /* list of filters as created from JSON */
+    ELLLIST pre_chain;        /* list of filters to be called pre-event-queue */
+    ELLLIST post_chain;       /* list of filters to be called post-event-queue */
+} dbChannel;
+
+/* Prototype for the post event function that is called in filter stacks */
+typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog);
+
+/* Return values from chFilterIf->parse_* routines: */
+typedef enum {
+    parse_stop, parse_continue
+} parse_result;
+
+/* These routines must be implemented by each filter plug-in */
+typedef struct chFilterIf {
+    /* Parsing event handlers: */
+    parse_result (* parse_start)(chFilter *filter);
+    /* If parse_start() returns parse_continue for a filter, one of
+     * parse_abort() or parse_end() will later be called for that same
+     * filter.
+     */
+    void (* parse_abort)(chFilter *filter);
+    /* If parse_abort() is called it should release any memory allocated
+     * for this filter; no further parse_...() calls will be made;
+     */
+    parse_result (* parse_end)(chFilter *filter);
+    /* If parse_end() returns parse_stop it should have released any
+     * memory allocated for this filter; no further parse_...() calls will
+     * be made in this case.
+     */
+
+    parse_result (* parse_null)(chFilter *filter);
+    parse_result (* parse_boolean)(chFilter *filter, int boolVal);
+    parse_result (* parse_integer)(chFilter *filter, long integerVal);
+    parse_result (* parse_double)(chFilter *filter, double doubleVal);
+    parse_result (* parse_string)(chFilter *filter, const char *stringVal,
+            size_t stringLen); /* NB: stringVal is not zero-terminated: */
+
+    parse_result (* parse_start_map)(chFilter *filter);
+    parse_result (* parse_map_key)(chFilter *filter, const char *key,
+            size_t stringLen); /* NB: key is not zero-terminated: */
+    parse_result (* parse_end_map)(chFilter *filter);
+
+    parse_result (* parse_start_array)(chFilter *filter);
+    parse_result (* parse_end_array)(chFilter *filter);
+
+    /* Channel operations: */
+    long (* channel_open)(chFilter *filter);
+    void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
+    void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
+    void (* channel_report)(chFilter *filter, int level, const unsigned short indent);
+    /* FIXME: More filter routines here ... */
+    void (* channel_close)(chFilter *filter);
+} chFilterIf;
+
+/* A chFilterPlugin holds data for a filter plugin */
+typedef struct chFilterPlugin {
+    ELLNODE node;
+    const char *name;
+    const chFilterIf *fif;
+    void *puser;
+} chFilterPlugin;
+
+/* A chFilter holds data for a single filter instance */
+struct chFilter {
+    ELLNODE list_node;
+    ELLNODE pre_node;
+    ELLNODE post_node;
+    dbChannel *chan;
+    const chFilterPlugin *plug;
+    chPostEventFunc *pre_func;
+    void *pre_arg;
+    chPostEventFunc *post_func;
+    void *post_arg;
+    void *puser;
+};
+
+struct dbCommon;
+struct dbFldDes;
+
+epicsShareFunc void dbChannelInit (void);
+epicsShareFunc long dbChannelTest(const char *name);
+epicsShareFunc dbChannel * dbChannelCreate(const char *name);
+epicsShareFunc long dbChannelOpen(dbChannel *chan);
+epicsShareFunc const char * dbChannelName(dbChannel *chan);
+epicsShareFunc struct dbCommon * dbChannelRecord(dbChannel *chan);
+epicsShareFunc struct dbFldDes * dbChannelFldDes(dbChannel *chan);
+epicsShareFunc long dbChannelElements(dbChannel *chan);
+epicsShareFunc short dbChannelFieldType(dbChannel *chan);
+epicsShareFunc short dbChannelExportType(dbChannel *chan);
+epicsShareFunc short dbChannelFieldSize(dbChannel *chan);
+epicsShareFunc long dbChannelFinalElements(dbChannel *chan);
+epicsShareFunc short dbChannelFinalFieldType(dbChannel *chan);
+epicsShareFunc short dbChannelFinalExportType(dbChannel *chan);
+epicsShareFunc short dbChannelFinalElementSize(dbChannel *chan);
+epicsShareFunc short dbChannelSpecial(dbChannel *chan);
+epicsShareFunc void * dbChannelField(dbChannel *chan);
+epicsShareFunc long dbChannelGet(dbChannel *chan, short type,
+        void *pbuffer, long *options, long *nRequest, void *pfl);
+epicsShareFunc long dbChannelGetField(dbChannel *chan, short type,
+        void *pbuffer, long *options, long *nRequest, void *pfl);
+epicsShareFunc long dbChannelPut(dbChannel *chan, short type,
+        const void *pbuffer, long nRequest);
+epicsShareFunc long dbChannelPutField(dbChannel *chan, short type,
+        const void *pbuffer, long nRequest);
+epicsShareFunc void dbChannelShow(dbChannel *chan, int level,
+        const unsigned short indent);
+epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level,
+        const unsigned short indent);
+epicsShareFunc void dbChannelDelete(dbChannel *chan);
+
+epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
+epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
+epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
+epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
+epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INC_dbChannel_H */

=== modified file 'src/ioc/db/dbChannelIO.cpp'
--- src/ioc/db/dbChannelIO.cpp	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbChannelIO.cpp	2012-05-30 18:10:27 +0000
@@ -4,20 +4,20 @@
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of 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.
 \*************************************************************************/
 
-/*  
+/*
  *  $Revision-Id$
  *
- *                              
+ *
  *                    L O S  A L A M O S
  *              Los Alamos National Laboratory
  *               Los Alamos, New Mexico 87545
- *                                  
+ *
  *  Copyright, 1986, The Regents of the University of California.
- *                                  
- *           
+ *
+ *
  *	Author Jeffrey O. Hill
  *	[email protected]
  *	505 665 1831
@@ -40,15 +40,12 @@
 #include "dbChannelIO.h"
 #include "dbPutNotifyBlocker.h"
 
-dbChannelIO::dbChannelIO ( 
-    epicsMutex & mutexIn, cacChannelNotify & notify, 
-    const dbAddr & addrIn, dbContext & serviceIO ) :
-    cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), 
-    addr ( addrIn )
+dbChannelIO::dbChannelIO (
+    epicsMutex & mutexIn, cacChannelNotify & notify,
+    dbChannel * dbchIn, dbContext & serviceIO ) :
+    cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ),
+    dbch ( dbchIn )
 {
-    unsigned bufLen = dbNameSizeOfPV ( & this->addr ) + 1;
-    this->pNameStr.reset ( new char [ bufLen ] );
-    dbNameOfPV ( & this->addr, this->pNameStr.get (), bufLen );
 }
 
 void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard )
@@ -57,7 +54,7 @@
     this->notify().connectNotify ( guard );
 }
 
-dbChannelIO::~dbChannelIO () 
+dbChannelIO::~dbChannelIO ()
 {
 }
 
@@ -65,48 +62,49 @@
 {
     guard.assertIdenticalMutex ( this->mutex );
     this->serviceIO.destroyAllIO ( guard, *this );
+    dbChannelDelete ( this->dbch );
     this->~dbChannelIO ();
 }
 
-void dbChannelIO::destroy ( 
+void dbChannelIO::destroy (
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );
     this->serviceIO.destroyChannel ( guard, *this );
-    // dont access this pointer after above call because
-    // object nolonger exists
+    // don't access this pointer after above call because
+    // object no longer exists
 }
 
-cacChannel::ioStatus dbChannelIO::read ( 
-     epicsGuard < epicsMutex > & guard, unsigned type, 
-     unsigned long count, cacReadNotify & notify, ioid * ) 
+cacChannel::ioStatus dbChannelIO::read (
+     epicsGuard < epicsMutex > & guard, unsigned type,
+     unsigned long count, cacReadNotify & notify, ioid * )
 {
     guard.assertIdenticalMutex ( this->mutex );
-    this->serviceIO.callReadNotify ( guard, this->addr, 
+    this->serviceIO.callReadNotify ( guard, this->dbch,
         type, count, notify );
     return iosSynch;
 }
 
-void dbChannelIO::write ( 
-    epicsGuard < epicsMutex > & guard, unsigned type, 
+void dbChannelIO::write (
+    epicsGuard < epicsMutex > & guard, unsigned type,
     unsigned long count, const void *pValue )
 {
     epicsGuardRelease < epicsMutex > unguard ( guard );
     if ( count > LONG_MAX ) {
         throw outOfBounds();
     }
-    int status = db_put_field ( &this->addr, type, pValue, 
+    int status = dbChannel_put ( this->dbch, type, pValue,
         static_cast <long> (count) );
     if ( status ) {
-        throw std::logic_error ( 
+        throw std::logic_error (
            "db_put_field() completed unsuccessfully" );
     }
 }
 
-cacChannel::ioStatus dbChannelIO::write ( 
-    epicsGuard < epicsMutex > & guard, unsigned type, 
-    unsigned long count, const void * pValue, 
-    cacWriteNotify & notify, ioid * pId ) 
+cacChannel::ioStatus dbChannelIO::write (
+    epicsGuard < epicsMutex > & guard, unsigned type,
+    unsigned long count, const void * pValue,
+    cacWriteNotify & notify, ioid * pId )
 {
     guard.assertIdenticalMutex ( this->mutex );
 
@@ -114,24 +112,24 @@
         throw outOfBounds();
     }
 
-    this->serviceIO.initiatePutNotify ( 
-        guard, *this, this->addr, 
+    this->serviceIO.initiatePutNotify (
+        guard, *this, this->dbch,
         type, count, pValue, notify, pId );
 
     return iosAsynch;
 }
 
-void dbChannelIO::subscribe ( 
-    epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, 
-    unsigned mask, cacStateNotify & notify, ioid * pId ) 
-{   
+void dbChannelIO::subscribe (
+    epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count,
+    unsigned mask, cacStateNotify & notify, ioid * pId )
+{
     guard.assertIdenticalMutex ( this->mutex );
-    this->serviceIO.subscribe ( 
-        guard, this->addr, *this,
+    this->serviceIO.subscribe (
+        guard, this->dbch, *this,
         type, count, mask, notify, pId );
 }
 
-void dbChannelIO::ioCancel ( 
+void dbChannelIO::ioCancel (
     epicsGuard < epicsMutex > & mutualExclusionGuard,
     const ioid & id )
 {
@@ -139,7 +137,7 @@
     this->serviceIO.ioCancel ( mutualExclusionGuard, *this, id );
 }
 
-void dbChannelIO::ioShow ( 
+void dbChannelIO::ioShow (
     epicsGuard < epicsMutex > & guard,
     const ioid & id, unsigned level ) const
 {
@@ -147,31 +145,35 @@
     this->serviceIO.ioShow ( guard, id, level );
 }
 
-void dbChannelIO::show ( 
+void dbChannelIO::show (
     epicsGuard < epicsMutex > & guard, unsigned level ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
 
-    printf ("channel at %p attached to local database record %s\n", 
-        static_cast <const void *> ( this ), this->addr.precord->name );
+    printf ("channel at %p attached to local database record %s\n",
+        static_cast <const void *> ( this ),
+        dbChannelRecord ( this->dbch ) -> name );
 
     if ( level > 0u ) {
-        printf ( "\ttype %s, element count %li, field at %p\n",
-            dbf_type_to_text ( this->addr.dbr_field_type ), this->addr.no_elements,
-            this->addr.pfield );
-    }
-    if ( level > 1u ) {
-        this->serviceIO.show ( level - 2u );
-        this->serviceIO.showAllIO ( *this, level - 2u );
+        printf ( "        type %s, element count %li, field at %p\n",
+            dbf_type_to_text ( dbChannelExportType ( this->dbch ) ),
+            dbChannelElements ( this->dbch ),
+            dbChannelField ( this->dbch ) );
+        if ( level > 1u ) {
+            dbChannelFilterShow ( this->dbch, level - 2u, 8 );
+            this->serviceIO.show ( level - 2u );
+            this->serviceIO.showAllIO ( *this, level - 2u );
+        }
     }
 }
 
 unsigned long dbChannelIO::nativeElementCount (
-    epicsGuard < epicsMutex > & guard ) const 
+    epicsGuard < epicsMutex > & guard ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
-    if ( this->addr.no_elements >= 0u ) {
-        return static_cast < unsigned long > ( this->addr.no_elements );
+    long elements = dbChannelElements ( this->dbch );
+    if ( elements >= 0u ) {
+        return static_cast < unsigned long > ( elements );
     }
     return 0u;
 }
@@ -181,24 +183,30 @@
     epicsGuard < epicsMutex > & guard ) const throw ()
 {
     guard.assertIdenticalMutex ( this->mutex );
-    return this->pNameStr.get ();
+    return dbChannelName ( this->dbch );
 }
 
 unsigned dbChannelIO::getName (
     epicsGuard < epicsMutex > &,
     char * pBuf, unsigned bufLen ) const throw ()
 {
-    return dbNameOfPV ( & this->addr, pBuf, bufLen );
+    const char *name = dbChannelName ( this->dbch );
+    size_t len = strlen ( name );
+    strncpy ( pBuf, name, bufLen );
+    if (len < bufLen)
+        return len;
+    pBuf[--bufLen] = '\0';
+    return bufLen;
 }
 
 short dbChannelIO::nativeType (
-    epicsGuard < epicsMutex > & guard ) const 
+    epicsGuard < epicsMutex > & guard ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
-    return this->addr.dbr_field_type;
+    return dbChannelExportType( this->dbch );
 }
 
-void * dbChannelIO::operator new ( size_t size, 
+void * dbChannelIO::operator new ( size_t size,
     tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList )
 {
     return freeList.allocate ( size );
@@ -212,7 +220,7 @@
 }
 
 #ifdef CXX_PLACEMENT_DELETE
-void dbChannelIO::operator delete ( void *pCadaver, 
+void dbChannelIO::operator delete ( void *pCadaver,
     tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList )
 {
     freeList.release ( pCadaver );

=== modified file 'src/ioc/db/dbChannelIO.h'
--- src/ioc/db/dbChannelIO.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbChannelIO.h	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 
 /*
@@ -44,65 +44,64 @@
 
 class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO {
 public:
-    dbChannelIO ( 
-        epicsMutex &, cacChannelNotify &, 
-        const dbAddr &, dbContext & );
-    void destructor ( 
+    dbChannelIO (
+        epicsMutex &, cacChannelNotify &,
+        dbChannel *, dbContext & );
+    void destructor (
         epicsGuard < epicsMutex > & );
     void destroy (
         epicsGuard < epicsMutex > & mutualExclusionGuard );
-    void callReadNotify ( 
-        epicsGuard < epicsMutex > &, 
-        unsigned type, unsigned long count, 
+    void callReadNotify (
+        epicsGuard < epicsMutex > &,
+        unsigned type, unsigned long count,
         cacReadNotify & notify );
-    void callStateNotify ( 
-        unsigned type, unsigned long count, 
+    void callStateNotify (
+        unsigned type, unsigned long count,
         const struct db_field_log * pfl, cacStateNotify & notify );
-    void show ( 
+    void show (
         epicsGuard < epicsMutex > &, unsigned level ) const;
     unsigned getName (
         epicsGuard < epicsMutex > &,
         char * pBuf, unsigned bufLen ) const throw ();
     const char * pName (
         epicsGuard < epicsMutex > & ) const throw ();
-    void * operator new ( size_t size, 
+    void * operator new ( size_t size,
         tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & );
-    epicsPlacementDeleteOperator (( void *, 
+    epicsPlacementDeleteOperator (( void *,
         tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ))
 protected:
     ~dbChannelIO ();
 private:
     epicsMutex & mutex;
     dbContext & serviceIO;
-    dbAddr addr;
-    epics_auto_ptr < char, eapt_array > pNameStr;
+    dbChannel * dbch;
 
     void initiateConnect (
         epicsGuard < epicsMutex > & );
-    unsigned requestMessageBytesPending ( 
-        epicsGuard < epicsMutex > & );
-    void flush ( 
-        epicsGuard < epicsMutex > & );
-    ioStatus read ( 
+    unsigned requestMessageBytesPending (
+        epicsGuard < epicsMutex > & );
+    void flush (
+        epicsGuard < epicsMutex > & );
+    ioStatus read (
         epicsGuard < epicsMutex > &,
-        unsigned type, unsigned long count, 
+        unsigned type, unsigned long count,
         cacReadNotify &, ioid * );
-    void write ( 
+    void write (
         epicsGuard < epicsMutex > &,
-        unsigned type, unsigned long count, 
+        unsigned type, unsigned long count,
         const void * pvalue );
-    ioStatus write ( 
+    ioStatus write (
         epicsGuard < epicsMutex > &,
-        unsigned type, unsigned long count, 
+        unsigned type, unsigned long count,
         const void * pvalue, cacWriteNotify &, ioid * );
-    void subscribe ( 
+    void subscribe (
         epicsGuard < epicsMutex > &,
-        unsigned type, unsigned long count, 
+        unsigned type, unsigned long count,
         unsigned mask, cacStateNotify &notify, ioid * );
-    void ioCancel ( 
+    void ioCancel (
         epicsGuard < epicsMutex > & mutualExclusionGuard,
         const ioid & );
-    void ioShow ( 
+    void ioShow (
         epicsGuard < epicsMutex > &,
         const ioid &, unsigned level ) const;
     short nativeType (
@@ -115,18 +114,18 @@
     void operator delete ( void * );
 };
 
-inline void dbChannelIO::callReadNotify ( 
-    epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, 
+inline void dbChannelIO::callReadNotify (
+    epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count,
     cacReadNotify & notify )
 {
     guard.assertIdenticalMutex ( this->mutex );
-    this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify );
+    this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify );
 }
 
-inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, 
+inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count,
         const struct db_field_log *pfl, cacStateNotify &notify )
 {
-    this->serviceIO.callStateNotify ( this->addr, type, count, pfl, notify );
+    this->serviceIO.callStateNotify ( this->dbch, type, count, pfl, notify );
 }
 
 

=== modified file 'src/ioc/db/dbContext.cpp'
--- src/ioc/db/dbContext.cpp	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbContext.cpp	2012-05-30 18:10:27 +0000
@@ -4,19 +4,19 @@
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of 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.
 \*************************************************************************/
-/*  
+/*
  *  $Revision-Id$
  *
- *                              
+ *
  *                    L O S  A L A M O S
  *              Los Alamos National Laboratory
  *               Los Alamos, New Mexico 87545
- *                                  
+ *
  *  Copyright, 1986, The Regents of the University of California.
- *                                  
- *           
+ *
+ *
  *	Author Jeffrey O. Hill
  *	[email protected]
  *	505 665 1831
@@ -33,6 +33,7 @@
 #include "epicsEvent.h"
 #include "epicsThread.h"
 #include "errlog.h"
+#include "dbChannel.h"
 
 #define epicsExportSharedSymbols
 #include "db_access_routines.h"
@@ -43,20 +44,20 @@
 class dbService : public cacService {
 public:
     ~dbService () {}
-    cacContext & contextCreate ( 
-        epicsMutex & mutualExclusion, 
-        epicsMutex & callbackControl, 
+    cacContext & contextCreate (
+        epicsMutex & mutualExclusion,
+        epicsMutex & callbackControl,
         cacContextNotify & );
 };
 
 static dbService dbs;
 
-cacContext & dbService::contextCreate ( 
-    epicsMutex & mutualExclusion, 
-    epicsMutex & callbackControl, 
+cacContext & dbService::contextCreate (
+    epicsMutex & mutualExclusion,
+    epicsMutex & callbackControl,
     cacContextNotify & notify )
 {
-    return * new dbContext ( callbackControl, 
+    return * new dbContext ( callbackControl,
         mutualExclusion, notify );
 }
 
@@ -67,9 +68,9 @@
 
 dbBaseIO::dbBaseIO () {}
 
-dbContext::dbContext ( epicsMutex & cbMutexIn, 
+dbContext::dbContext ( epicsMutex & cbMutexIn,
         epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
-    readNotifyCache ( mutexIn ), ctx ( 0 ), 
+    readNotifyCache ( mutexIn ), ctx ( 0 ),
     stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
     notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 )
 {
@@ -84,41 +85,41 @@
 }
 
 cacChannel & dbContext::createChannel ( // X aCC 361
-    epicsGuard < epicsMutex > & guard, const char * pName, 
+    epicsGuard < epicsMutex > & guard, const char * pName,
     cacChannelNotify & notifyIn, cacChannel::priLev priority )
 {
     guard.assertIdenticalMutex ( this->mutex );
 
-    struct dbAddr addr;
-    int status;
-    {
-        // dont know if the database might call a put callback 
-        // while holding its lock ...
-        epicsGuardRelease < epicsMutex > unguard ( guard );
-        status = db_name_to_addr ( pName, & addr );
-    }
-    if ( status ) {
+    dbChannel *dbch = dbChannel_create ( pName );
+    if ( ! dbch ) {
         if ( ! this->pNetContext.get() ) {
             this->pNetContext.reset (
-                & this->notify.createNetworkContext ( 
+                & this->notify.createNetworkContext (
                     this->mutex, this->cbMutex ) );
         }
         return this->pNetContext->createChannel (
                     guard, pName, notifyIn, priority );
     }
-    else if ( ca_preemtive_callback_is_enabled () ) {
-        return * new ( this->dbChannelIOFreeList )
-            dbChannelIO ( this->mutex, notifyIn, addr, *this ); 
-    }
-    else {
-        errlogPrintf ( 
+
+    if ( ! ca_preemtive_callback_is_enabled () ) {
+        dbChannelDelete ( dbch );
+        errlogPrintf (
             "dbContext: preemptive callback required for direct in\n"
             "memory interfacing of CA channels to the DB.\n" );
         throw cacChannel::unsupportedByService ();
     }
+
+    try {
+        return * new ( this->dbChannelIOFreeList )
+            dbChannelIO ( this->mutex, notifyIn, dbch, *this );
+    }
+    catch (...) {
+        dbChannelDelete ( dbch );
+        throw;
+    }
 }
 
-void dbContext::destroyChannel ( 
+void dbContext::destroyChannel (
     epicsGuard < epicsMutex > & guard, dbChannelIO & chan )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -134,30 +135,30 @@
     this->dbChannelIOFreeList.release ( & chan );
 }
 
-void dbContext::callStateNotify ( struct dbAddr & addr, 
-        unsigned type, unsigned long count, 
-        const struct db_field_log * pfl, 
+void dbContext::callStateNotify ( struct dbChannel * dbch,
+        unsigned type, unsigned long count,
+        const struct db_field_log * pfl,
         cacStateNotify & notifyIn )
 {
     unsigned long size = dbr_size_n ( type, count );
 
     if ( type > INT_MAX ) {
         epicsGuard < epicsMutex > guard ( this->mutex );
-        notifyIn.exception ( guard, ECA_BADTYPE, 
-            "type code out of range (high side)", 
+        notifyIn.exception ( guard, ECA_BADTYPE,
+            "type code out of range (high side)",
             type, count );
         return;
     }
 
     if ( count > INT_MAX ) {
         epicsGuard < epicsMutex > guard ( this->mutex );
-        notifyIn.exception ( guard, ECA_BADCOUNT, 
+        notifyIn.exception ( guard, ECA_BADCOUNT,
             "element count out of range (high side)",
             type, count);
         return;
     }
 
-    // no need to lock this because state notify is 
+    // no need to lock this because state notify is
     // called from only one event queue consumer thread
     if ( this->stateNotifyCacheSize < size) {
         char * pTmp = new char [size];
@@ -166,14 +167,14 @@
         this->stateNotifyCacheSize = size;
     }
     void *pvfl = (void *) pfl;
-    int status = db_get_field ( &addr, static_cast <int> ( type ), 
+    int status = dbChannel_get ( dbch, static_cast <int> ( type ),
                     this->pStateNotifyCache, static_cast <int> ( count ), pvfl );
     if ( status ) {
         epicsGuard < epicsMutex > guard ( this->mutex );
-        notifyIn.exception ( guard, ECA_GETFAIL, 
-            "db_get_field() completed unsuccessfuly", type, count );
+        notifyIn.exception ( guard, ECA_GETFAIL,
+            "dbChannel_get() completed unsuccessfully", type, count );
     }
-    else { 
+    else {
         epicsGuard < epicsMutex > guard ( this->mutex );
         notifyIn.current ( guard, type, count, this->pStateNotifyCache );
     }
@@ -185,10 +186,10 @@
     assert ( status == ECA_NORMAL );
 }
 
-void dbContext::subscribe ( 
+void dbContext::subscribe (
     epicsGuard < epicsMutex > & guard,
-    struct dbAddr & addr, dbChannelIO & chan,
-    unsigned type, unsigned long count, unsigned mask, 
+    struct dbChannel * dbch, dbChannelIO & chan,
+    unsigned type, unsigned long count, unsigned mask,
     cacStateNotify & notifyIn, cacChannel::ioid * pId )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -214,12 +215,12 @@
 
             unsigned selfPriority = epicsThreadGetPrioritySelf ();
             unsigned above;
-            epicsThreadBooleanStatus tbs = 
+            epicsThreadBooleanStatus tbs =
                 epicsThreadLowestPriorityLevelAbove ( selfPriority, &above );
             if ( tbs != epicsThreadBooleanStatusSuccess ) {
                 above = selfPriority;
             }
-            int status = db_start_events ( tmpctx, "CAC-event", 
+            int status = db_start_events ( tmpctx, "CAC-event",
                 cacAttachClientCtx, ca_current_context (), above );
             if ( status ) {
                 db_close_events ( tmpctx );
@@ -227,7 +228,7 @@
             }
         }
         if ( this->ctx ) {
-            // another thread tried to simultaneously setup 
+            // another thread tried to simultaneously setup
             // the event system
             db_close_events ( tmpctx );
         }
@@ -237,9 +238,9 @@
     }
 
     dbSubscriptionIO & subscr =
-        * new ( this->dbSubscriptionIOFreeList ) 
-        dbSubscriptionIO ( guard, this->mutex, *this, chan, 
-            addr, notifyIn, type, count, mask, this->ctx );
+        * new ( this->dbSubscriptionIOFreeList )
+        dbSubscriptionIO ( guard, this->mutex, *this, chan,
+            dbch, notifyIn, type, count, mask, this->ctx );
     chan.dbContextPrivateListOfIO::eventq.add ( subscr );
     this->ioTable.idAssignAdd ( subscr );
 
@@ -248,27 +249,27 @@
     }
 }
 
-void dbContext::initiatePutNotify ( 
+void dbContext::initiatePutNotify (
     epicsGuard < epicsMutex > & guard,
-    dbChannelIO & chan, struct dbAddr & addr, 
-    unsigned type, unsigned long count, const void * pValue, 
+    dbChannelIO & chan, struct dbChannel * dbch,
+    unsigned type, unsigned long count, const void * pValue,
     cacWriteNotify & notifyIn, cacChannel::ioid * pId )
 {
     guard.assertIdenticalMutex ( this->mutex );
     if ( ! chan.dbContextPrivateListOfIO::pBlocker ) {
-        chan.dbContextPrivateListOfIO::pBlocker = 
-            new ( this->dbPutNotifyBlockerFreeList ) 
+        chan.dbContextPrivateListOfIO::pBlocker =
+            new ( this->dbPutNotifyBlockerFreeList )
                 dbPutNotifyBlocker ( this->mutex );
         this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker );
     }
-    chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( 
-        guard, notifyIn, addr, type, count, pValue );
+    chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify (
+        guard, notifyIn, dbch, type, count, pValue );
     if ( pId ) {
         *pId = chan.dbContextPrivateListOfIO::pBlocker->getId ();
     }
 }
 
-void dbContext::destroyAllIO ( 
+void dbContext::destroyAllIO (
     epicsGuard < epicsMutex > & guard, dbChannelIO & chan )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -284,7 +285,7 @@
     }
 
     while ( ( pIO = tmp.get() ) ) {
-        // This prevents a db event callback from coming 
+        // This prevents a db event callback from coming
         // through after the notify IO is deleted
         pIO->unsubscribe ( guard );
         // If they call ioCancel() here it will be ignored
@@ -301,8 +302,8 @@
     }
 }
 
-void dbContext::ioCancel ( 
-    epicsGuard < epicsMutex > & guard, dbChannelIO & chan, 
+void dbContext::ioCancel (
+    epicsGuard < epicsMutex > & guard, dbChannelIO & chan,
     const cacChannel::ioid &id )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -325,8 +326,8 @@
     }
 }
 
-void dbContext::ioShow ( 
-    epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, 
+void dbContext::ioShow (
+    epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id,
     unsigned level ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -339,7 +340,7 @@
 void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const
 {
     epicsGuard < epicsMutex > guard ( this->mutex );
-    tsDLIterConst < dbSubscriptionIO > pItem = 
+    tsDLIterConst < dbSubscriptionIO > pItem =
         chan.dbContextPrivateListOfIO::eventq.firstIter ();
     while ( pItem.valid () ) {
         pItem->show ( guard, level );
@@ -356,14 +357,14 @@
     this->show ( guard, level );
 }
 
-void dbContext::show ( 
+void dbContext::show (
     epicsGuard < epicsMutex > & guard, unsigned level ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
-    printf ( "dbContext at %p\n", 
+    printf ( "dbContext at %p\n",
         static_cast <const void *> ( this ) );
     if ( level > 0u ) {
-        printf ( "\tevent call back cache location %p, and its size %lu\n", 
+        printf ( "\tevent call back cache location %p, and its size %lu\n",
             static_cast <void *> ( this->pStateNotifyCache ), this->stateNotifyCacheSize );
         this->readNotifyCache.show ( guard, level - 1 );
     }
@@ -375,7 +376,7 @@
     }
 }
 
-void dbContext::flush ( 
+void dbContext::flush (
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );

=== modified file 'src/ioc/db/dbContextReadNotifyCache.cpp'
--- src/ioc/db/dbContextReadNotifyCache.cpp	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbContextReadNotifyCache.cpp	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 
 /*
@@ -30,11 +30,11 @@
 
 class privateAutoDestroyPtr {
 public:
-    privateAutoDestroyPtr ( 
+    privateAutoDestroyPtr (
         dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) :
         _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {}
     ~privateAutoDestroyPtr () { _allocator.free ( _p ); }
-	char * get () const { return _p; }              
+	char * get () const { return _p; }
 private:
     dbContextReadNotifyCacheAllocator & _allocator;
     char * _p;
@@ -43,28 +43,28 @@
 };
 
 // extra effort taken here to not hold the lock when calling the callback
-void dbContextReadNotifyCache::callReadNotify ( 
-    epicsGuard < epicsMutex > & guard, struct dbAddr & addr, 
+void dbContextReadNotifyCache::callReadNotify (
+    epicsGuard < epicsMutex > & guard, struct dbChannel * dbch,
 	unsigned type, unsigned long count, cacReadNotify & notify )
 {
     guard.assertIdenticalMutex ( _mutex );
 
     if ( type > INT_MAX ) {
-        notify.exception ( guard, ECA_BADTYPE, 
-            "type code out of range (high side)", 
+        notify.exception ( guard, ECA_BADTYPE,
+            "type code out of range (high side)",
             type, count );
         return;
     }
 
-    if ( addr.no_elements < 0 ) {
-        notify.exception ( guard, ECA_BADCOUNT, 
+    if ( dbChannelElements(dbch) < 0 ) {
+        notify.exception ( guard, ECA_BADCOUNT,
             "database has negetive element count",
             type, count);
         return;
     }
 
-    if ( count > static_cast < unsigned > ( addr.no_elements ) ) {
-        notify.exception ( guard, ECA_BADCOUNT, 
+    if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) {
+        notify.exception ( guard, ECA_BADCOUNT,
             "element count out of range (high side)",
             type, count);
         return;
@@ -75,21 +75,21 @@
     int status;
     {
         epicsGuardRelease < epicsMutex > unguard ( guard );
-        status = db_get_field ( &addr, static_cast <int> ( type ), 
-                    ptr.get (), static_cast <int> ( count ), 0 );
+        status = dbChannel_get ( dbch, static_cast <int> ( type ),
+                    ptr.get (), static_cast <long> ( count ), 0 );
     }
     if ( status ) {
-        notify.exception ( guard, ECA_GETFAIL, 
+        notify.exception ( guard, ECA_GETFAIL,
             "db_get_field() completed unsuccessfuly",
             type, count );
     }
-    else { 
-        notify.completion ( 
+    else {
+        notify.completion (
             guard, type, count, ptr.get () );
     }
 }
 
-void dbContextReadNotifyCache::show ( 
+void dbContextReadNotifyCache::show (
     epicsGuard < epicsMutex > & guard, unsigned level ) const
 {
     guard.assertIdenticalMutex ( _mutex );
@@ -155,8 +155,8 @@
             pNext = _pReadNotifyCache->pNext;
             count++;
         }
-        printf ( "\tcount %lu and size %lu\n", 
-            static_cast < unsigned long > ( count ), 
+        printf ( "\tcount %lu and size %lu\n",
+            static_cast < unsigned long > ( count ),
             _readNotifyCacheSize );
     }
 }

=== modified file 'src/ioc/db/dbEvent.c'
--- src/ioc/db/dbEvent.c	2011-10-19 18:07:00 +0000
+++ src/ioc/db/dbEvent.c	2012-05-30 18:10:27 +0000
@@ -1,19 +1,20 @@
 /*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE 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.
 \*************************************************************************/
-/* dbEvent.c */
-/* $Revision-Id$ */
-/* routines for scheduling events to lower priority tasks via the RT kernel */
+
 /*
- *  Author:     Jeffrey O. Hill 
- *      Date:            4-1-89
-*/
+ *  Author: Jeffrey O. Hill <[email protected]>
+ *
+ *          Ralph Lange <[email protected]>
+ */
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -39,6 +40,7 @@
 #include "db_field_log.h"
 #define epicsExportSharedSymbols
 #include "dbAddr.h"
+#include "dbChannel.h"
 #include "dbLock.h"
 #include "dbAccessDefs.h"
 #include "dbEvent.h"
@@ -49,51 +51,33 @@
 #define EVENTQEMPTY     ((struct evSubscrip *)NULL)
 
 /*
- * event subscruiption
- */
-struct evSubscrip {
-    ELLNODE                 node;
-    struct dbAddr           *paddr;
-    EVENTFUNC               *user_sub;
-    void                    *user_arg;
-    struct event_que        *ev_que;
-    db_field_log            *pLastLog;
-    unsigned long           npend;  /* n times this event is on the queue */
-    unsigned long           nreplace;  /* n times replacing event on the queue */
-    unsigned char           select;
-    char                    valque;
-    char                    callBackInProgress;
-    char                    enabled;
-};
-
-/*
  * really a ring buffer
  */
 struct event_que {
     /* lock writers to the ring buffer only */
     /* readers must never slow up writers */
     epicsMutexId            writelock;
-    db_field_log            valque[EVENTQUESIZE];
+    db_field_log            *valque[EVENTQUESIZE];
     struct evSubscrip       *evque[EVENTQUESIZE];
     struct event_que        *nextque;       /* in case que quota exceeded */
     struct event_user       *evUser;        /* event user parent struct */
     unsigned short          putix;
     unsigned short          getix;
     unsigned short          quota;          /* the number of assigned entries*/
-    unsigned short          nDuplicates;    /* N events duplicated on this q */ 
+    unsigned short          nDuplicates;    /* N events duplicated on this q */
     unsigned short          nCanceled;      /* the number of canceled entries */
 };
 
 struct event_user {
     struct event_que    firstque;       /* the first event que */
-    
+
     epicsMutexId        lock;
     epicsEventId        ppendsem;       /* Wait while empty */
     epicsEventId        pflush_sem;     /* wait for flush */
-        
+
     EXTRALABORFUNC      *extralabor_sub;/* off load to event task */
     void                *extralabor_arg;/* parameter to above */
-    
+
     epicsThreadId       taskid;         /* event handler task id */
     struct evSubscrip   *pSuicideEvent; /* event that is deleteing itself */
     unsigned            queovr;         /* event que overflow count */
@@ -114,34 +98,28 @@
 #define RNGINC(OLD)\
 ( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) )
 
-#define LOCKEVQUE(EV_QUE)\
-epicsMutexMustLock((EV_QUE)->writelock);
-
-#define UNLOCKEVQUE(EV_QUE)\
-epicsMutexUnlock((EV_QUE)->writelock);
-
-#define LOCKREC(RECPTR)\
-epicsMutexMustLock((RECPTR)->mlok);
-
-#define UNLOCKREC(RECPTR)\
-epicsMutexUnlock((RECPTR)->mlok);
+#define LOCKEVQUE(EV_QUE)   epicsMutexMustLock((EV_QUE)->writelock)
+#define UNLOCKEVQUE(EV_QUE) epicsMutexUnlock((EV_QUE)->writelock)
+#define LOCKREC(RECPTR)     epicsMutexMustLock((RECPTR)->mlok)
+#define UNLOCKREC(RECPTR)   epicsMutexUnlock((RECPTR)->mlok)
 
 static void *dbevEventUserFreeList;
 static void *dbevEventQueueFreeList;
-static void *dbevEventBlockFreeList;
+static void *dbevEventSubscriptionFreeList;
+static void *dbevFieldLogFreeList;
 
 static char *EVENT_PEND_NAME = "eventTask";
 
 static struct evSubscrip canceledEvent;
 
-static unsigned short ringSpace ( const struct event_que *pevq ) 
+static unsigned short ringSpace ( const struct event_que *pevq )
 {
     if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) {
         if ( pevq->getix > pevq->putix ) {
             return ( unsigned short ) ( pevq->getix - pevq->putix );
         }
         else {
-            return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); 
+            return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix );
         }
     }
     return 0;
@@ -163,7 +141,7 @@
     DBADDR              addr;
     long                status;
     struct evSubscrip   *pevent;
-    dbFldDes 		    *pdbFldDes;
+    dbFldDes            *pdbFldDes;
 
     if ( ! pname ) return DB_EVENT_OK;
     status = dbNameToAddr ( pname, &addr );
@@ -172,7 +150,7 @@
         return DB_EVENT_ERROR;
     }
 
-    LOCKREC ( addr.precord );
+    LOCKREC (addr.precord);
 
     pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis );
 
@@ -186,16 +164,16 @@
         ellCount ( &addr.precord->mlis ) );
 
     while ( pevent ) {
-	    pdbFldDes = pevent->paddr->pfldDes;
+        pdbFldDes = dbChannelFldDes(pevent->chan);
 
         if ( level > 0 ) {
 	        printf ( "%4.4s", pdbFldDes->name );
 
 	        printf ( " { " );
-	        if ( pevent->select & DBE_VALUE ) printf( "VALUE " );
-	        if ( pevent->select & DBE_LOG ) printf( "LOG " );
-	        if ( pevent->select & DBE_ALARM ) printf( "ALARM " );
-	        if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " );
+                if ( pevent->select & DBE_VALUE ) printf( "VALUE " );
+                if ( pevent->select & DBE_LOG ) printf( "LOG " );
+                if ( pevent->select & DBE_ALARM ) printf( "ALARM " );
+                if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " );
 	        printf ( "}" );
 
             if ( pevent->npend ) {
@@ -210,15 +188,15 @@
                 taskId = ( void * ) pevent->ev_que->evUser->taskid;
                 UNLOCKEVQUE(pevent->ev_que);
                 if ( nEntriesFree == 0u ) {
-                    printf ( ", thread=%p, queue full", 
+                    printf ( ", thread=%p, queue full",
                         (void *) taskId );
                 }
                 else if ( nEntriesFree == EVENTQUESIZE ) {
-                    printf ( ", thread=%p, queue empty", 
+                    printf ( ", thread=%p, queue empty",
                         (void *) taskId );
                 }
                 else {
-                    printf ( ", thread=%p, unused entries=%u", 
+                    printf ( ", thread=%p, unused entries=%u",
                         (void *) taskId, nEntriesFree );
                 }
             }
@@ -229,7 +207,7 @@
                 if ( pevent->nreplace ) {
                     printf (", discarded by replacement=%ld", pevent->nreplace);
                 }
-                if ( ! pevent->valque ) {
+                if ( ! pevent->useValque ) {
                     printf (", queueing disabled" );
                 }
                 LOCKEVQUE(pevent->ev_que);
@@ -245,19 +223,19 @@
             }
 
             if ( level > 3 ) {
-                printf ( ", ev %p, ev que %p, ev user %p", 
-                    ( void * ) pevent, 
-                    ( void * ) pevent->ev_que, 
+                printf ( ", ev %p, ev que %p, ev user %p",
+                    ( void * ) pevent,
+                    ( void * ) pevent->ev_que,
                     ( void * ) pevent->ev_que->evUser );
             }
 
 	        printf( "\n" );
         }
 
-	    pevent = (struct evSubscrip *) ellNext ( &pevent->node );
+            pevent = (struct evSubscrip *) ellNext ( &pevent->node );
     }
 
-    UNLOCKREC ( addr.precord );
+    UNLOCKREC (addr.precord);
 
     return DB_EVENT_OK;
 }
@@ -268,32 +246,36 @@
  *
  * Initialize the event facility for this task. Must be called at least once
  * by each task which uses the db event facility
- * 
+ *
  * returns: ptr to event user block or NULL if memory can't be allocated
  */
 dbEventCtx epicsShareAPI db_init_events (void)
 {
     struct event_user * evUser;
-    
+
     if (!dbevEventUserFreeList) {
-        freeListInitPvt(&dbevEventUserFreeList, 
+        freeListInitPvt(&dbevEventUserFreeList,
             sizeof(struct event_user),8);
     }
     if (!dbevEventQueueFreeList) {
-        freeListInitPvt(&dbevEventQueueFreeList, 
+        freeListInitPvt(&dbevEventQueueFreeList,
             sizeof(struct event_que),8);
     }
-    if (!dbevEventBlockFreeList) {
-        freeListInitPvt(&dbevEventBlockFreeList, 
+    if (!dbevEventSubscriptionFreeList) {
+        freeListInitPvt(&dbevEventSubscriptionFreeList,
             sizeof(struct evSubscrip),256);
     }
-    
-    evUser = (struct event_user *) 
+    if (!dbevFieldLogFreeList) {
+        freeListInitPvt(&dbevFieldLogFreeList,
+            sizeof(struct db_field_log),2048);
+    }
+
+    evUser = (struct event_user *)
         freeListCalloc(dbevEventUserFreeList);
     if (!evUser) {
         return NULL;
     }
-    
+
     evUser->firstque.evUser = evUser;
     evUser->firstque.writelock = epicsMutexCreate();
     if (!evUser->firstque.writelock) {
@@ -304,7 +286,7 @@
     if (!evUser->ppendsem) {
         epicsMutexDestroy (evUser->firstque.writelock);
         return NULL;
-    }    
+    }
     evUser->pflush_sem = epicsEventCreate(epicsEventEmpty);
     if (!evUser->pflush_sem) {
         epicsMutexDestroy (evUser->firstque.writelock);
@@ -327,7 +309,7 @@
 
 /*
  *  DB_CLOSE_EVENTS()
- *  
+ *
  *  evUser block and additional event queues
  *  deallocated when the event thread terminates
  *  itself
@@ -341,7 +323,7 @@
      * Exit not forced on event blocks for now - this is left to channel
      * access and any other tasks using this facility which can find them
      * more efficiently.
-     * 
+     *
      * NOTE: not deleting events before calling this routine could be
      * hazardous to the system's health.
      */
@@ -375,7 +357,7 @@
  * DB_ADD_EVENT()
  */
 dbEventSubscription epicsShareAPI db_add_event (
-    dbEventCtx ctx, struct dbAddr *paddr,
+    dbEventCtx ctx, struct dbChannel *chan,
     EVENTFUNC *user_sub, void *user_arg, unsigned select)
 {
     struct event_user * const evUser = (struct event_user *) ctx;
@@ -389,7 +371,7 @@
         return NULL;
     }
 
-    pevent = freeListCalloc ( dbevEventBlockFreeList );
+    pevent = freeListCalloc (dbevEventSubscriptionFreeList);
     if ( ! pevent ) {
         return NULL;
     }
@@ -422,7 +404,7 @@
     epicsMutexUnlock ( evUser->lock );
 
     if ( ! ev_que ) {
-        freeListFree ( dbevEventBlockFreeList, pevent );
+        freeListFree ( dbevEventSubscriptionFreeList, pevent );
         return NULL;
     }
 
@@ -430,7 +412,7 @@
     pevent->nreplace =  0ul;
     pevent->user_sub =  user_sub;
     pevent->user_arg =  user_arg;
-    pevent->paddr =     paddr;
+    pevent->chan =      chan;
     pevent->select =    (unsigned char) select;
     pevent->pLastLog =  NULL; /* not yet in the queue */
     pevent->callBackInProgress = FALSE;
@@ -442,12 +424,12 @@
      * communication (for other types they get whatever happens to be
      * there upon wakeup)
      */
-    if( paddr->no_elements == 1 && 
-        paddr->field_size <= sizeof(union native_value)) {
-        pevent->valque = TRUE;
+    if( dbChannelElements(chan) == 1 &&
+        dbChannelFieldSize(chan) <= sizeof(union native_value)) {
+        pevent->useValque = TRUE;
     }
     else {
-        pevent->valque = FALSE;
+        pevent->useValque = FALSE;
     }
 
     return pevent;
@@ -456,93 +438,93 @@
 /*
  * db_event_enable()
  */
-void epicsShareAPI db_event_enable (dbEventSubscription es)
+void epicsShareAPI db_event_enable (dbEventSubscription event)
 {
-    struct evSubscrip * const pevent = (struct evSubscrip *) es;
-    struct dbCommon * const precord = 
-                (struct dbCommon *) pevent->paddr->precord;
+    struct evSubscrip * const pevent = (struct evSubscrip *) event;
+    struct dbCommon * const precord = dbChannelRecord(pevent->chan);
 
-    LOCKREC(precord);
+    LOCKREC (precord);
     if ( ! pevent->enabled ) {
         ellAdd (&precord->mlis, &pevent->node);
         pevent->enabled = TRUE;
     }
-    UNLOCKREC(precord);
+    UNLOCKREC (precord);
 }
 
 /*
  * db_event_disable()
  */
-void epicsShareAPI db_event_disable (dbEventSubscription es)
+void epicsShareAPI db_event_disable (dbEventSubscription event)
 {
-    struct evSubscrip * const pevent = (struct evSubscrip *) es;
-    struct dbCommon * const precord = 
-                (struct dbCommon *) pevent->paddr->precord;
+    struct evSubscrip * const pevent = (struct evSubscrip *) event;
+    struct dbCommon * const precord = dbChannelRecord(pevent->chan);
 
-    LOCKREC(precord);
+    LOCKREC (precord);
     if ( pevent->enabled ) {
         ellDelete(&precord->mlis, &pevent->node);
         pevent->enabled = FALSE;
     }
-    UNLOCKREC(precord);
+    UNLOCKREC (precord);
 }
 
 /*
  * event_remove()
  * event queue lock _must_ be applied
+ * this nulls the entry in the queue, but doesn't delete the db_field_log chunk
  */
-static void event_remove ( struct event_que *ev_que, 
+static void event_remove ( struct event_que *ev_que,
     unsigned short index, struct evSubscrip *placeHolder )
 {
-    struct evSubscrip * const pEvent = ev_que->evque[index];
+    struct evSubscrip * const pevent = ev_que->evque[index];
 
     ev_que->evque[index] = placeHolder;
-    if ( pEvent->npend == 1u ) {
-        pEvent->pLastLog = NULL;
+    ev_que->valque[index] = NULL;
+    if ( pevent->npend == 1u ) {
+        pevent->pLastLog = NULL;
     }
     else {
-        assert ( pEvent->npend > 1u );
+        assert ( pevent->npend > 1u );
         assert ( ev_que->nDuplicates >= 1u );
         ev_que->nDuplicates--;
     }
-    pEvent->npend--;
+    pevent->npend--;
 }
 
 /*
  * DB_CANCEL_EVENT()
  *
- * This routine does not prevent two threads from deleting 
+ * This routine does not prevent two threads from deleting
  * the same block at the same time.
- * 
+ *
  */
-void epicsShareAPI db_cancel_event (dbEventSubscription es)
+void epicsShareAPI db_cancel_event (dbEventSubscription event)
 {
-    struct evSubscrip * const pevent = ( struct evSubscrip * ) es;
+    struct evSubscrip * const pevent = (struct evSubscrip *) event;
     unsigned short getix;
 
-    db_event_disable ( es );
+    db_event_disable ( event );
 
     /*
-     * flag the event as canceled by NULLing out the callback handler 
+     * flag the event as canceled by NULLing out the callback handler
      *
      * make certain that the event isnt being accessed while
      * its call back changes
      */
-    LOCKEVQUE ( pevent->ev_que )
+    LOCKEVQUE (pevent->ev_que);
 
     pevent->user_sub = NULL;
 
     /*
      * purge this event from the queue
      *
-     * Its better to take this approach rather than waiting 
+     * Its better to take this approach rather than waiting
      * for the event thread to finish removing this event
      * from the queue because the event thread will not
-     * process if we are in flow control mode. Since blocking 
+     * process if we are in flow control mode. Since blocking
      * here will block CA's TCP input queue then a dead lock
      * would be possible.
      */
-    for (   getix = pevent->ev_que->getix; 
+    for (   getix = pevent->ev_que->getix;
             pevent->ev_que->evque[getix] != EVENTQEMPTY; ) {
         if ( pevent->ev_que->evque[getix] == pevent ) {
             assert ( pevent->ev_que->nCanceled < USHRT_MAX );
@@ -561,17 +543,17 @@
     }
     else {
         while ( pevent->callBackInProgress ) {
-            UNLOCKEVQUE ( pevent->ev_que )
+            UNLOCKEVQUE (pevent->ev_que);
             epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem );
-            LOCKEVQUE ( pevent->ev_que )
+            LOCKEVQUE (pevent->ev_que);
         }
     }
 
     pevent->ev_que->quota -= EVENTENTRIES;
 
-    UNLOCKEVQUE ( pevent->ev_que )
-        
-    freeListFree ( dbevEventBlockFreeList, pevent );
+    UNLOCKEVQUE (pevent->ev_que);
+
+    freeListFree ( dbevEventSubscriptionFreeList, pevent );
 
     return;
 }
@@ -585,7 +567,7 @@
 {
     struct event_user * const evUser = (struct event_user *) ctx;
 
-	epicsMutexMustLock ( evUser->lock );
+    epicsMutexMustLock ( evUser->lock );
     while ( evUser->extraLaborBusy ) {
         epicsMutexUnlock ( evUser->lock );
         epicsThreadSleep(0.1);
@@ -640,38 +622,90 @@
 }
 
 /*
- *  DB_POST_SINGLE_EVENT_PRIVATE()
+ *  DB_CREATE_EVENT_LOG()
  *
  *  NOTE: This assumes that the db scan lock is already applied
- */
-static void db_post_single_event_private (struct evSubscrip *event)
-{  
-    struct event_que * const ev_que = event->ev_que;
-    db_field_log * pLog;
+ *        (as it copies data from the record)
+ */
+db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent)
+{
+    db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
+
+    if (pLog) {
+        struct dbChannel *chan = pevent->chan;
+        struct dbCommon  *prec = dbChannelRecord(chan);
+        pLog->ctx = dbfl_context_event;
+        if (pevent->useValque) {
+            pLog->type = dbfl_type_val;
+            pLog->stat = prec->stat;
+            pLog->sevr = prec->sevr;
+            pLog->time = prec->time;
+            pLog->field_type  = dbChannelFieldType(chan);
+            pLog->no_elements = dbChannelElements(chan);
+            /*
+             * use memcpy to avoid a bus error on
+             * union copy of char in the db at an odd
+             * address
+             */
+            memcpy(&pLog->u.v.field,
+                   dbChannelField(chan),
+                   dbChannelFieldSize(chan));
+        } else {
+            pLog->type = dbfl_type_rec;
+        }
+    }
+    return pLog;
+}
+
+/*
+ *  DB_CREATE_READ_LOG()
+ *
+ */
+db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan)
+{
+    db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
+
+    if (pLog) {
+        pLog->ctx  = dbfl_context_read;
+        pLog->type = dbfl_type_rec;
+    }
+    return pLog;
+}
+
+/*
+ *  DB_QUEUE_EVENT_LOG()
+ *
+ */
+static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
+{
+    struct event_que    *ev_que;
     int firstEventFlag;
     unsigned rngSpace;
 
+    ev_que = pevent->ev_que;
     /*
      * evUser ring buffer must be locked for the multiple
      * threads writing/reading it
      */
 
-    LOCKEVQUE(ev_que)
+    LOCKEVQUE (ev_que);
 
     /*
-     * if we have an event on the queue and we are
-     * not saving the current value (because this is a
-     * string or an array) then ignore duplicate 
-     * events (saving them without the current valuye
-     * serves no purpose)
+     * if we have an event on the queue and both the last
+     * event on the queue and the current event are emtpy
+     * (i.e. of type dbfl_type_rec), simply ignore duplicate
+     * events (saving empty events serves no purpose)
      */
-    if (!event->valque && event->npend>0u) {
-        UNLOCKEVQUE(ev_que)
+    if (pevent->npend > 0u &&
+        (*pevent->pLastLog)->type == dbfl_type_rec &&
+        pLog->type == dbfl_type_rec) {
+        db_delete_field_log(pLog);
+        UNLOCKEVQUE (ev_que);
         return;
     }
 
-    /* 
-     * add to task local event que 
+    /*
+     * add to task local event que
      */
 
     /*
@@ -680,34 +714,38 @@
      * then replace the last event on the queue (for this monitor)
      */
     rngSpace = ringSpace ( ev_que );
-    if ( event->npend>0u && 
+    if ( pevent->npend>0u &&
         (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) {
         /*
          * replace last event if no space is left
          */
-        pLog = event->pLastLog;
-        event->nreplace++;
+        if (*pevent->pLastLog) {
+            db_delete_field_log(*pevent->pLastLog);
+            *pevent->pLastLog = pLog;
+        }
+        pevent->nreplace++;
         /*
-         * the event task has already been notified about 
+         * the event task has already been notified about
          * this so we dont need to post the semaphore
          */
         firstEventFlag = 0;
     }
     /*
- 	 * Otherwise, the current entry must be available.
+     * Otherwise, the current entry must be available.
      * Fill it in and advance the ring buffer.
      */
     else {
         assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY );
-        pLog = &ev_que->valque[ev_que->putix];
-        ev_que->evque[ev_que->putix] = event;
-        if (event->npend>0u) {
+        ev_que->evque[ev_que->putix] = pevent;
+        ev_que->valque[ev_que->putix] = pLog;
+        pevent->pLastLog = &ev_que->valque[ev_que->putix];
+        if (pevent->npend>0u) {
             ev_que->nDuplicates++;
         }
-        event->npend++;
-        /* 
-         * if the ring buffer was empty before 
-         * adding this event 
+        pevent->npend++;
+        /*
+         * if the ring buffer was empty before
+         * adding this event
          */
         if (rngSpace==EVENTQUESIZE) {
             firstEventFlag = 1;
@@ -718,39 +756,21 @@
         ev_que->putix = RNGINC ( ev_que->putix );
     }
 
-    if (pLog && event->valque) {
-        struct dbCommon *precord = event->paddr->precord;
-        pLog->stat = precord->stat;
-        pLog->sevr = precord->sevr;
-        pLog->time = precord->time;
-
-        /*
-         * use memcpy to avoid a bus error on
-         * union copy of char in the db at an odd 
-         * address
-         */
-        memcpy( (char *)&pLog->field,
-            (char *)event->paddr->pfield,
-            event->paddr->field_size);
-
-        event->pLastLog = pLog;
-    }
-
-    UNLOCKEVQUE(ev_que)
-
-    /* 
-     * its more efficent to notify the event handler 
+    UNLOCKEVQUE (ev_que);
+
+    /*
+     * its more efficent to notify the event handler
      * only after the event is ready and the lock
      * is off in case it runs at a higher priority
      * than the caller here.
      */
     if (firstEventFlag) {
-        /* 
-         * notify the event handler 
+        /*
+         * notify the event handler
          */
         epicsEventSignal(ev_que->evUser->ppendsem);
     }
-} 
+}
 
 /*
  *  DB_POST_EVENTS()
@@ -763,28 +783,30 @@
 void            *pField,
 unsigned int    caEventMask
 )
-{  
-    struct dbCommon * const pdbc = (struct dbCommon *)pRecord;
-    struct evSubscrip * event;
-
-    if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */
-
-    LOCKREC(pdbc);
-  
-    for (event = (struct evSubscrip *) pdbc->mlis.node.next;
-        event; event = (struct evSubscrip *) event->node.next){
-        
+{
+    struct dbCommon   * const prec = (struct dbCommon *) pRecord;
+    struct evSubscrip *pevent;
+
+    if (prec->mlis.count == 0) return DB_EVENT_OK;       /* no monitors set */
+
+    LOCKREC (prec);
+
+    for (pevent = (struct evSubscrip *) prec->mlis.node.next;
+        pevent; pevent = (struct evSubscrip *) pevent->node.next){
+
         /*
          * Only send event msg if they are waiting on the field which
          * changed or pval==NULL and waiting on alarms and alarms changed
          */
-        if ( (event->paddr->pfield == (void *)pField || pField==NULL) &&
-            (caEventMask & event->select) ) {
-            db_post_single_event_private (event);
+        if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) &&
+            (caEventMask & pevent->select)) {
+            db_field_log *pLog = db_create_event_log(pevent);
+            pLog = dbChannelRunPreChain(pevent->chan, pLog);
+            if (pLog) db_queue_event_log(pevent, pLog);
         }
     }
 
-    UNLOCKREC(pdbc);
+    UNLOCKREC (prec);
     return DB_EVENT_OK;
 
 }
@@ -792,14 +814,18 @@
 /*
  *  DB_POST_SINGLE_EVENT()
  */
-void epicsShareAPI db_post_single_event (dbEventSubscription es)
-{  
-    struct evSubscrip * const event = (struct evSubscrip *) es;
-    struct dbCommon * const precord = event->paddr->precord;
-
-    dbScanLock (precord);
-    db_post_single_event_private (event);
-    dbScanUnlock (precord);
+void epicsShareAPI db_post_single_event (dbEventSubscription event)
+{
+    struct evSubscrip * const pevent = (struct evSubscrip *) event;
+    struct dbCommon * const prec = dbChannelRecord(pevent->chan);
+
+    dbScanLock (prec);
+
+    db_field_log *pLog = db_create_event_log(pevent);
+    pLog = dbChannelRunPreChain(pevent->chan, pLog);
+    db_queue_event_log(pevent, pLog);
+
+    dbScanUnlock (prec);
 }
 
 /*
@@ -808,31 +834,35 @@
 static int event_read ( struct event_que *ev_que )
 {
     db_field_log *pfl;
-    void ( *user_sub ) ( void *user_arg, struct dbAddr *paddr, 
+    void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
             int eventsRemaining, db_field_log *pfl );
-    
+
     /*
      * evUser ring buffer must be locked for the multiple
      * threads writing/reading it
      */
-    LOCKEVQUE ( ev_que )
-        
+    LOCKEVQUE (ev_que);
+
     /*
      * if in flow control mode drain duplicates and then
      * suspend processing events until flow control
      * mode is over
      */
     if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) {
-        UNLOCKEVQUE(ev_que);
+        UNLOCKEVQUE (ev_que);
         return DB_EVENT_OK;
     }
-    
+
     while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) {
-        db_field_log fl = ev_que->valque[ev_que->getix];
-        struct evSubscrip *event = ev_que->evque[ev_que->getix];
+        pfl = ev_que->valque[ev_que->getix];
+        struct evSubscrip *pevent = ev_que->evque[ev_que->getix];
 
-        if ( event == &canceledEvent ) {
+        if ( pevent == &canceledEvent ) {
             ev_que->evque[ev_que->getix] = EVENTQEMPTY;
+            if (ev_que->valque[ev_que->getix]) {
+                db_delete_field_log(ev_que->valque[ev_que->getix]);
+                ev_que->valque[ev_que->getix] = NULL;
+            }
             ev_que->getix = RNGINC ( ev_que->getix );
             assert ( ev_que->nCanceled > 0 );
             ev_que->nCanceled--;
@@ -844,12 +874,6 @@
          * communication. (for other types they get whatever happens
          * to be there upon wakeup)
          */
-        if ( event->valque ) {
-            pfl = &fl;
-        }
-        else {
-            pfl = NULL;
-        }
 
         event_remove ( ev_que, ev_que->getix, EVENTQEMPTY );
         ev_que->getix = RNGINC ( ev_que->getix );
@@ -858,29 +882,34 @@
          * create a local copy of the call back parameters while
          * we still have the lock
          */
-        user_sub = event->user_sub;
+        user_sub = pevent->user_sub;
 
         /*
          * Next event pointer can be used by event tasks to determine
          * if more events are waiting in the queue
          *
          * Must remove the lock here so that we dont deadlock if
-         * this calls dbGetField() and blocks on the record lock, 
-         * dbPutField() is in progress in another task, it has the 
-         * record lock, and it is calling db_post_events() waiting 
+         * this calls dbGetField() and blocks on the record lock,
+         * dbPutField() is in progress in another task, it has the
+         * record lock, and it is calling db_post_events() waiting
          * for the event queue lock (which this thread now has).
          */
         if ( user_sub ) {
             /*
              * This provides a way to test to see if an event is in use
-             * despite the fact that the event queue does not point to 
-             * it. 
+             * despite the fact that the event queue does not point to
+             * it.
              */
-            event->callBackInProgress = TRUE;
-            UNLOCKEVQUE ( ev_que )
-            ( *user_sub ) ( event->user_arg, event->paddr, 
+            pevent->callBackInProgress = TRUE;
+            UNLOCKEVQUE (ev_que);
+            /* Run post-event-queue filter chain */
+            if (ellCount(&pevent->chan->post_chain)) {
+                dbChannelRunPostChain(pevent->chan, pfl);
+            }
+            /* Issue user callback */
+            ( *user_sub ) ( pevent->user_arg, pevent->chan,
                 ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl );
-            LOCKEVQUE ( ev_que )
+            LOCKEVQUE (ev_que);
 
             /*
              * check to see if this event has been canceled each
@@ -889,22 +918,23 @@
              * complete sem if there are no longer any events on the
              * queue
              */
-            if ( ev_que->evUser->pSuicideEvent == event ) {
+            if ( ev_que->evUser->pSuicideEvent == pevent ) {
                 ev_que->evUser->pSuicideEvent = NULL;
             }
             else {
-                if ( event->user_sub==NULL && event->npend==0u ) {
-                    event->callBackInProgress = FALSE;
+                if ( pevent->user_sub==NULL && pevent->npend==0u ) {
+                    pevent->callBackInProgress = FALSE;
                     epicsEventSignal ( ev_que->evUser->pflush_sem );
                 }
                 else {
-                    event->callBackInProgress = FALSE;
+                    pevent->callBackInProgress = FALSE;
                 }
             }
         }
+        db_delete_field_log(pfl);
     }
 
-    UNLOCKEVQUE ( ev_que )
+    UNLOCKEVQUE (ev_que);
 
     return DB_EVENT_OK;
 }
@@ -924,7 +954,7 @@
 
     taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL );
 
-    do{
+    do {
         void (*pExtraLaborSub) (void *);
         void *pExtraLaborArg;
         epicsEventMustWait(evUser->ppendsem);
@@ -951,7 +981,7 @@
         }
         evUser->extraLaborBusy = FALSE;
 
-        for ( ev_que = &evUser->firstque; ev_que; 
+        for ( ev_que = &evUser->firstque; ev_que;
                 ev_que = ev_que->nextque ) {
             epicsMutexUnlock ( evUser->lock );
             event_read (ev_que);
@@ -959,7 +989,7 @@
         }
         epicsMutexUnlock ( evUser->lock );
 
-    } while( ! evUser->pendexit );
+    } while ( ! evUser->pendexit );
 
     epicsMutexDestroy(evUser->firstque.writelock);
 
@@ -967,7 +997,7 @@
         struct event_que    *nextque;
 
         ev_que = evUser->firstque.nextque;
-        while(ev_que){ 
+        while (ev_que) {
             nextque = ev_que->nextque;
             epicsMutexDestroy(ev_que->writelock);
             freeListFree(dbevEventQueueFreeList, ev_que);
@@ -990,15 +1020,15 @@
  * DB_START_EVENTS()
  */
 int epicsShareAPI db_start_events (
-    dbEventCtx ctx,const char *taskname, void (*init_func)(void *), 
+    dbEventCtx ctx,const char *taskname, void (*init_func)(void *),
     void *init_func_arg, unsigned osiPriority )
 {
      struct event_user * const evUser = (struct event_user *) ctx;
-     
+
      epicsMutexMustLock ( evUser->lock );
 
-     /* 
-      * only one ca_pend_event thread may be 
+     /*
+      * only one ca_pend_event thread may be
       * started for each evUser
       */
      if (evUser->taskid) {
@@ -1044,7 +1074,7 @@
     epicsMutexMustLock ( evUser->lock );
     evUser->flowCtrlMode = TRUE;
     epicsMutexUnlock ( evUser->lock );
-    /* 
+    /*
      * notify the event handler task
      */
     epicsEventSignal(evUser->ppendsem);
@@ -1063,13 +1093,24 @@
     epicsMutexMustLock ( evUser->lock );
     evUser->flowCtrlMode = FALSE;
     epicsMutexUnlock ( evUser->lock );
-    /* 
+    /*
      * notify the event handler task
      */
     epicsEventSignal (evUser->ppendsem);
 #ifdef DEBUG
     printf("fc off %lu\n", tickGet());
 #endif
-} 
-
-
+}
+
+/*
+ * db_delete_field_log()
+ */
+void epicsShareAPI db_delete_field_log (db_field_log *pfl)
+{
+    if (pfl) {
+        /* Free field if reference type field log and dtor is set */
+        if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl);
+        /* Free the field log chunk */
+        freeListFree(dbevFieldLogFreeList, pfl);
+    }
+}

=== modified file 'src/ioc/db/dbEvent.h'
--- src/ioc/db/dbEvent.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbEvent.h	2012-05-30 18:10:27 +0000
@@ -1,17 +1,19 @@
 /*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE 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.
 \*************************************************************************/
-/* 
- *      $Revision-Id$
+
+/*
+ *  Author: Jeffrey O. Hill <[email protected]>
  *
- *      Author: Jeff Hill 
- *      Date: 	030393 
+ *          Ralph Lange <[email protected]>
  */
 
 #ifndef INCLdbEventh
@@ -34,8 +36,9 @@
 extern "C" {
 #endif
 
-struct dbAddr;
+struct dbChannel;
 struct db_field_log;
+struct evSubscrip;
 
 epicsShareFunc int epicsShareAPI db_event_list (
     const char *name, unsigned level);
@@ -49,7 +52,7 @@
 typedef void EXTRALABORFUNC (void *extralabor_arg);
 epicsShareFunc dbEventCtx epicsShareAPI db_init_events (void);
 epicsShareFunc int epicsShareAPI db_start_events (
-    dbEventCtx ctx, const char *taskname, void (*init_func)(void *), 
+    dbEventCtx ctx, const char *taskname, void (*init_func)(void *),
     void *init_func_arg, unsigned osiPriority );
 epicsShareFunc void epicsShareAPI db_close_events (dbEventCtx ctx);
 epicsShareFunc void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx);
@@ -60,18 +63,22 @@
 epicsShareFunc int epicsShareAPI db_post_extra_labor (dbEventCtx ctx);
 epicsShareFunc void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority );
 
-typedef void EVENTFUNC (void *user_arg, struct dbAddr *paddr,
+typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan,
 	int eventsRemaining, struct db_field_log *pfl);
 
 typedef void * dbEventSubscription;
 epicsShareFunc dbEventSubscription epicsShareAPI db_add_event (
-    dbEventCtx ctx, struct dbAddr *paddr,
+    dbEventCtx ctx, struct dbChannel *chan,
     EVENTFUNC *user_sub, void *user_arg, unsigned select);
 epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es);
 epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es);
 epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es);
 epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es);
 
+epicsShareFunc struct db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent);
+epicsShareFunc struct db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan);
+epicsShareFunc void epicsShareAPI db_delete_field_log (struct db_field_log *pfl);
+
 #define DB_EVENT_OK 0
 #define DB_EVENT_ERROR (-1)
 

=== added file 'src/ioc/db/dbExtractArray.c'
--- src/ioc/db/dbExtractArray.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbExtractArray.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,84 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ *
+ *  based on dbConvert.c
+ *  written by: Bob Dalesio, Marty Kraimer
+ */
+
+#include <string.h>
+
+#include "epicsTypes.h"
+#include "dbAddr.h"
+#define epicsExportSharedSymbols
+#include "dbExtractArray.h"
+
+void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
+                           long nRequest, long no_elements, long offset, long increment)
+{
+    char *pdst = (char *) pto;
+    char *psrc = (char *) paddr->pfield;
+    long nUpperPart;
+    int i;
+    short srcSize = paddr->field_size;
+    short dstSize = srcSize;
+    char isString = (paddr->field_type == DBF_STRING);
+
+    if (nRequest > no_elements) nRequest = no_elements;
+    if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
+
+    if (increment == 1 && dstSize == srcSize) {
+        nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
+        memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
+        if (nRequest > nUpperPart)
+            memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
+        if (isString)
+            for (i = 1; i <= nRequest; i++)
+                pdst[dstSize*i-1] = '\0';
+    } else {
+        for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
+            offset %= no_elements;
+            memcpy(pdst, &psrc[offset*srcSize], dstSize);
+            if (isString) pdst[dstSize-1] = '\0';
+        }
+    }
+}
+
+void dbExtractArrayFromBuf(const void *pfrom, void *pto,
+                           short field_size, short field_type,
+                           long nRequest, long no_elements, long offset, long increment)
+{
+    char *pdst = (char *) pto;
+    char *psrc = (char *) pfrom;
+    int i;
+    short srcSize = field_size;
+    short dstSize = srcSize;
+    char isString = (field_type == DBF_STRING);
+
+    if (nRequest > no_elements) nRequest = no_elements;
+    if (offset > no_elements - 1) offset = no_elements - 1;
+    if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
+
+    if (increment == 1) {
+        memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
+        if (isString)
+            for (i = 1; i <= nRequest; i++)
+                pdst[dstSize*i] = '\0';
+    } else {
+        for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
+            memcpy(pdst, &psrc[offset*srcSize], dstSize);
+            if (isString) pdst[dstSize] = '\0';
+        }
+    }
+}

=== added file 'src/ioc/db/dbExtractArray.h'
--- src/ioc/db/dbExtractArray.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbExtractArray.h	2012-05-30 18:10:27 +0000
@@ -0,0 +1,34 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#ifndef INC_dbExtractArray_H
+#define INC_dbExtractArray_H
+
+#include "dbFldTypes.h"
+#include "dbAddr.h"
+#include "shareLib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
+                                          long nRequest, long no_elements, long offset, long increment);
+epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
+                                          short field_size, short field_type,
+                                          long nRequest, long no_elements, long offset, long increment);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // INC_dbExtractArray_H

=== modified file 'src/ioc/db/dbIocRegister.c'
--- src/ioc/db/dbIocRegister.c	2012-04-10 22:10:50 +0000
+++ src/ioc/db/dbIocRegister.c	2012-05-30 18:10:27 +0000
@@ -21,6 +21,7 @@
 #include "dbNotify.h"
 #include "callback.h"
 #include "dbIocRegister.h"
+#include "dbState.h"
 
 /* dbLoadDatabase */
 static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString};
@@ -297,6 +298,51 @@
     callbackSetQueueSize(args[0].ival);
 }
 
+/* dbStateCreate */
+static const iocshArg dbStateCreateArg0 = { "name", iocshArgString };
+static const iocshArg * const dbStateCreateArgs[] = { &dbStateCreateArg0 };
+static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs };
+static void dbStateCreateCallFunc (const iocshArgBuf *args)
+{
+    dbStateCreate(args[0].sval);
+}
+
+/* dbStateSet */
+static const iocshArg dbStateSetArg0 = { "id", iocshArgInt };
+static const iocshArg * const dbStateSetArgs[] = { &dbStateSetArg0 };
+static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs };
+static void dbStateSetCallFunc (const iocshArgBuf *args)
+{
+    dbStateSet((dbStateId) args[0].ival);
+}
+
+/* dbStateClear */
+static const iocshArg dbStateClearArg0 = { "id", iocshArgInt };
+static const iocshArg * const dbStateClearArgs[] = { &dbStateClearArg0 };
+static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs };
+static void dbStateClearCallFunc (const iocshArgBuf *args)
+{
+    dbStateClear((dbStateId) args[0].ival);
+}
+
+/* dbStateShow */
+static const iocshArg dbStateShowArg0 = { "id", iocshArgInt };
+static const iocshArg dbStateShowArg1 = { "level", iocshArgInt };
+static const iocshArg * const dbStateShowArgs[] = { &dbStateShowArg0, &dbStateShowArg1 };
+static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs };
+static void dbStateShowCallFunc (const iocshArgBuf *args)
+{
+    dbStateShow((dbStateId) args[0].ival, args[1].ival);
+}
+
+/* dbStateShowAll */
+static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt };
+static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 };
+static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs };
+static void dbStateShowAllCallFunc (const iocshArgBuf *args)
+{
+    dbStateShowAll(args[0].ival);
+}
 
 void epicsShareAPI dbIocRegister(void)
 {
@@ -342,4 +388,10 @@
     iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
 
     iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
+
+    iocshRegister(&dbStateCreateFuncDef, dbStateCreateCallFunc);
+    iocshRegister(&dbStateSetFuncDef, dbStateSetCallFunc);
+    iocshRegister(&dbStateClearFuncDef, dbStateClearCallFunc);
+    iocshRegister(&dbStateShowFuncDef, dbStateShowCallFunc);
+    iocshRegister(&dbStateShowAllFuncDef, dbStateShowAllCallFunc);
 }

=== added file 'src/ioc/db/dbLink.c'
--- src/ioc/db/dbLink.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbLink.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,648 @@
+/*************************************************************************\
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
+ *     National Laboratory.
+ * Copyright (c) 2002 The Regents of the University of California, as
+ *     Operator of Los Alamos National Laboratory.
+ * EPICS BASE is distributed subject to a Software License Agreement found
+ * in file LICENSE that is included with this distribution.
+ \*************************************************************************/
+/* dbLink.c */
+/* $Id$ */
+/*
+ *      Original Authors: Bob Dalesio, Marty Kraimer
+ *      Current Author: Andrew Johnson
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dbDefs.h"
+#include "epicsThread.h"
+#include "errlog.h"
+#include "cantProceed.h"
+#include "cvtFast.h"
+#include "epicsTime.h"
+#include "alarm.h"
+#include "ellLib.h"
+#include "dbStaticLib.h"
+#include "dbBase.h"
+#include "link.h"
+#include "dbFldTypes.h"
+#include "recSup.h"
+#include "devSup.h"
+#include "caeventmask.h"
+#include "db_field_log.h"
+#include "dbCommon.h"
+#include "dbFldTypes.h"
+#include "special.h"
+#include "errMdef.h"
+#define epicsExportSharedSymbols
+#include "dbAddr.h"
+#include "dbLink.h"
+#include "callback.h"
+#include "dbScan.h"
+#include "dbLock.h"
+#include "dbEvent.h"
+#include "dbConvert.h"
+#include "dbConvertFast.h"
+#include "epicsEvent.h"
+#include "dbCa.h"
+#include "dbBkpt.h"
+#include "dbNotify.h"
+#include "dbAccessDefs.h"
+#include "recGbl.h"
+
+static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
+        epicsEnum16 stat, epicsEnum16 sevr)
+{
+    switch (ppv_link->pvlMask & pvlOptMsMode) {
+    case pvlOptNMS:
+        break;
+    case pvlOptMSI:
+        if (sevr < INVALID_ALARM)
+            break;
+        /* Fall through */
+    case pvlOptMS:
+        recGblSetSevr(pdest, LINK_ALARM, sevr);
+        break;
+    case pvlOptMSS:
+        recGblSetSevr(pdest, stat, sevr);
+        break;
+    }
+}
+
+/***************************** Constant Links *****************************/
+
+static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer)
+{
+    if (!plink->value.constantStr)
+        return S_db_badField;
+
+    /* Constant strings are always numeric */
+    if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
+        dbrType = DBF_USHORT;
+
+    return dbFastPutConvertRoutine[DBR_STRING][dbrType]
+            (plink->value.constantStr, pbuffer, NULL);
+}
+
+static long dbConstGetNelements(const struct link *plink, long *nelements)
+{
+    *nelements = 0;
+    return 0;
+}
+
+static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
+        epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
+{
+    if (pnRequest)
+        *pnRequest = 0;
+    return 0;
+}
+
+/***************************** Database Links *****************************/
+
+static long dbDbInitLink(struct link *plink, short dbfType)
+{
+    DBADDR dbaddr;
+    long status;
+    DBADDR *pdbAddr;
+
+    status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
+    if (status)
+        return status;
+
+    plink->type = DB_LINK;
+    pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
+    *pdbAddr = dbaddr; /* structure copy */
+    plink->value.pv_link.pvt = pdbAddr;
+    return 0;
+}
+
+static long dbDbAddLink(struct link *plink, short dbfType)
+{
+    long status = dbDbInitLink(plink, dbfType);
+
+    if (!status) {
+        DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
+        dbCommon *precord = plink->value.pv_link.precord;
+
+        dbLockSetRecordLock(pdbAddr->precord);
+        dbLockSetMerge(precord, pdbAddr->precord);
+    }
+    return status;
+}
+
+static void dbDbRemoveLink(struct link *plink)
+{
+    free(plink->value.pv_link.pvt);
+    plink->value.pv_link.pvt = 0;
+    plink->value.pv_link.getCvt = 0;
+    plink->value.pv_link.lastGetdbrType = 0;
+    plink->type = PV_LINK;
+    dbLockSetSplit(plink->value.pv_link.precord);
+}
+
+static int dbDbIsLinkConnected(const struct link *plink)
+{
+    return TRUE;
+}
+
+static int dbDbGetDBFtype(const struct link *plink)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+
+    return paddr->field_type;
+}
+
+static long dbDbGetElements(const struct link *plink, long *nelements)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+
+    *nelements = paddr->no_elements;
+    return 0;
+}
+
+static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
+        epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
+{
+    struct pv_link *ppv_link = &plink->value.pv_link;
+    DBADDR *paddr = ppv_link->pvt;
+    dbCommon *precord = plink->value.pv_link.precord;
+    long status;
+
+    /* scan passive records if link is process passive  */
+    if (ppv_link->pvlMask & pvlOptPP) {
+        unsigned char pact = precord->pact;
+
+        precord->pact = TRUE;
+        status = dbScanPassive(precord, paddr->precord);
+        precord->pact = pact;
+        if (status)
+            return status;
+    }
+    *pstat = paddr->precord->stat;
+    *psevr = paddr->precord->sevr;
+
+    if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
+        status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
+    } else {
+        unsigned short dbfType = paddr->field_type;
+
+        if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
+            return S_db_badDbrtype;
+
+        if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
+                && paddr->special != SPC_ATTRIBUTE) {
+            ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
+            status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
+        } else {
+            ppv_link->getCvt = NULL;
+            status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
+        }
+        ppv_link->lastGetdbrType = dbrType;
+    }
+    return status;
+}
+
+static long dbDbGetControlLimits(const struct link *plink, double *low,
+        double *high)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+    struct buffer {
+        DBRctrlDouble
+        double value;
+    } buffer;
+    long options = DBR_CTRL_DOUBLE;
+    long number_elements = 0;
+    long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
+            NULL);
+
+    if (status)
+        return status;
+
+    *low = buffer.lower_ctrl_limit;
+    *high = buffer.upper_ctrl_limit;
+    return 0;
+}
+
+static long dbDbGetGraphicLimits(const struct link *plink, double *low,
+        double *high)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+    struct buffer {
+        DBRgrDouble
+        double value;
+    } buffer;
+    long options = DBR_GR_DOUBLE;
+    long number_elements = 0;
+    long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
+            NULL);
+
+    if (status)
+        return status;
+
+    *low = buffer.lower_disp_limit;
+    *high = buffer.upper_disp_limit;
+    return 0;
+}
+
+static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
+        double *low, double *high, double *hihi)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+    struct buffer {
+        DBRalDouble
+        double value;
+    } buffer;
+    long options = DBR_AL_DOUBLE;
+    long number_elements = 0;
+    long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
+            0);
+
+    if (status)
+        return status;
+
+    *lolo = buffer.lower_alarm_limit;
+    *low = buffer.lower_warning_limit;
+    *high = buffer.upper_warning_limit;
+    *hihi = buffer.upper_alarm_limit;
+    return 0;
+}
+
+static long dbDbGetPrecision(const struct link *plink, short *precision)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+    struct buffer {
+        DBRprecision
+        double value;
+    } buffer;
+    long options = DBR_PRECISION;
+    long number_elements = 0;
+    long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
+            0);
+
+    if (status)
+        return status;
+
+    *precision = buffer.precision.dp;
+    return 0;
+}
+
+static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+    struct buffer {
+        DBRunits
+        double value;
+    } buffer;
+    long options = DBR_UNITS;
+    long number_elements = 0;
+    long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
+            0);
+
+    if (status)
+        return status;
+
+    strncpy(units, buffer.units, unitsSize);
+    return 0;
+}
+
+static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
+        epicsEnum16 *severity)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+
+    if (status)
+        *status = paddr->precord->stat;
+    if (severity)
+        *severity = paddr->precord->sevr;
+    return 0;
+}
+
+static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
+{
+    DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
+
+    *pstamp = paddr->precord->time;
+    return 0;
+}
+
+static long dbDbPutValue(struct link *plink, short dbrType,
+        const void *pbuffer, long nRequest)
+{
+    struct pv_link *ppv_link = &plink->value.pv_link;
+    struct dbCommon *psrce = ppv_link->precord;
+    DBADDR *paddr = (DBADDR *) ppv_link->pvt;
+    dbCommon *pdest = paddr->precord;
+    long status = dbPut(paddr, dbrType, pbuffer, nRequest);
+
+    inherit_severity(ppv_link, pdest, psrce->nsta, psrce->nsev);
+    if (status)
+        return status;
+
+    if (paddr->pfield == (void *) &pdest->proc ||
+            (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
+        /* if dbPutField caused asyn record to process */
+        /* ask for reprocessing*/
+        if (pdest->putf) {
+            pdest->rpro = TRUE;
+        } else { /* process dest record with source's PACT true */
+            unsigned char pact;
+
+            if (psrce && psrce->ppn)
+                dbNotifyAdd(psrce, pdest);
+            pact = psrce->pact;
+            psrce->pact = TRUE;
+            status = dbProcess(pdest);
+            psrce->pact = pact;
+        }
+    }
+    return status;
+}
+
+static void dbDbScanFwdLink(struct link *plink)
+{
+    dbCommon *precord = plink->value.pv_link.precord;
+    dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
+
+    dbScanPassive(precord, paddr->precord);
+}
+
+lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
+        dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
+        dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
+        dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
+        dbDbPutValue, dbDbScanFwdLink };
+
+/***************************** Generic Link API *****************************/
+
+void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
+{
+    plink->value.pv_link.precord = precord;
+
+    if (plink == &precord->tsel)
+        recGblTSELwasModified(plink);
+
+    if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
+        /* Make it a DB link if possible */
+        if (!dbDbInitLink(plink, dbfType))
+            return;
+    }
+
+    /* Make it a CA link */
+    if (dbfType == DBF_INLINK)
+        plink->value.pv_link.pvlMask |= pvlOptInpNative;
+
+    dbCaAddLink(plink);
+    if (dbfType == DBF_FWDLINK) {
+        char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
+
+        if (pperiod && strstr(pperiod, "PROC")) {
+            plink->value.pv_link.pvlMask |= pvlOptFWD;
+        } else {
+            errlogPrintf("%s.FLNK is a Channel Access Link "
+                " but does not link to a PROC field\n", precord->name);
+        }
+    }
+}
+
+void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
+{
+    plink->value.pv_link.precord = precord;
+
+    if (plink == &precord->tsel)
+        recGblTSELwasModified(plink);
+
+    if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
+        /* Can we make it a DB link? */
+        if (!dbDbAddLink(plink, dbfType))
+            return;
+    }
+
+    /* Make it a CA link */
+    if (dbfType == DBF_INLINK)
+        plink->value.pv_link.pvlMask |= pvlOptInpNative;
+
+    dbCaAddLink(plink);
+    if (dbfType == DBF_FWDLINK) {
+        char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
+
+        if (pperiod && strstr(pperiod, "PROC"))
+            plink->value.pv_link.pvlMask |= pvlOptFWD;
+    }
+}
+
+long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
+{
+    switch (plink->type) {
+    case CONSTANT:
+        return dbConstLoadLink(plink, dbrType, pbuffer);
+    }
+    return S_db_notFound;
+}
+
+void dbRemoveLink(struct link *plink)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        dbDbRemoveLink(plink);
+        break;
+    case CA_LINK:
+        dbCaRemoveLink(plink);
+        break;
+    default:
+        cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type);
+    }
+    plink->type = PV_LINK;
+    plink->value.pv_link.pvlMask = 0;
+}
+
+int dbIsLinkConnected(const struct link *plink)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbIsLinkConnected(plink);
+    case CA_LINK:
+        return dbCaIsLinkConnected(plink);
+    }
+    return FALSE;
+}
+
+int dbGetLinkDBFtype(const struct link *plink)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetDBFtype(plink);
+    case CA_LINK:
+        return dbCaGetLinkDBFtype(plink);
+    }
+    return -1;
+}
+
+long dbGetNelements(const struct link *plink, long *nelements)
+{
+    switch (plink->type) {
+    case CONSTANT:
+        return dbConstGetNelements(plink, nelements);
+    case DB_LINK:
+        return dbDbGetElements(plink, nelements);
+    case CA_LINK:
+        return dbCaGetNelements(plink, nelements);
+    }
+    return S_db_badField;
+}
+
+long dbGetLinkValue(struct link *plink, short dbrType, void *pbuffer,
+        long *poptions, long *pnRequest)
+{
+    struct dbCommon *precord = plink->value.pv_link.precord;
+    epicsEnum16 sevr = 0, stat = 0;
+    long status;
+
+    if (poptions && *poptions) {
+        printf("dbGetLinkValue: Use of poptions no longer supported\n");
+        *poptions = 0;
+    }
+
+    switch (plink->type) {
+    case CONSTANT:
+        status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr,
+                pnRequest);
+        break;
+    case DB_LINK:
+        status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
+        break;
+    case CA_LINK:
+        status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
+        break;
+    default:
+        cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type);
+        status = -1;
+    }
+    if (status) {
+        recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
+    } else {
+        inherit_severity(&plink->value.pv_link, precord, stat, sevr);
+    }
+    return status;
+}
+
+long dbGetControlLimits(const struct link *plink, double *low, double *high)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetControlLimits(plink, low, high);
+    case CA_LINK:
+        return dbCaGetControlLimits(plink, low, high);
+    }
+    return S_db_notFound;
+}
+
+long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetGraphicLimits(plink, low, high);
+    case CA_LINK:
+        return dbCaGetGraphicLimits(plink, low, high);
+    }
+    return S_db_notFound;
+}
+
+long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
+        double *high, double *hihi)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetAlarmLimits(plink, lolo, low, high, hihi);
+    case CA_LINK:
+        return dbCaGetAlarmLimits(plink, lolo, low, high, hihi);
+    }
+    return S_db_notFound;
+}
+
+long dbGetPrecision(const struct link *plink, short *precision)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetPrecision(plink, precision);
+    case CA_LINK:
+        return dbCaGetPrecision(plink, precision);
+    }
+    return S_db_notFound;
+}
+
+long dbGetUnits(const struct link *plink, char *units, int unitsSize)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetUnits(plink, units, unitsSize);
+    case CA_LINK:
+        return dbCaGetUnits(plink, units, unitsSize);
+    }
+    return S_db_notFound;
+}
+
+long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
+        epicsEnum16 *severity)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetAlarm(plink, status, severity);
+    case CA_LINK:
+        return dbCaGetAlarm(plink, status, severity);
+    }
+    return S_db_notFound;
+}
+
+long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        return dbDbGetTimeStamp(plink, pstamp);
+    case CA_LINK:
+        return dbCaGetTimeStamp(plink, pstamp);
+    }
+    return S_db_notFound;
+}
+
+long dbPutLinkValue(struct link *plink, short dbrType, const void *pbuffer,
+        long nRequest)
+{
+    long status;
+
+    switch (plink->type) {
+    case DB_LINK:
+        status = dbDbPutValue(plink, dbrType, pbuffer, nRequest);
+        break;
+    case CA_LINK:
+        status = dbCaPutLink(plink, dbrType, pbuffer, nRequest);
+        break;
+    default:
+        cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type);
+        status = -1;
+    }
+    if (status) {
+        struct dbCommon *precord = plink->value.pv_link.precord;
+
+        recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
+    }
+    return status;
+}
+
+void dbScanFwdLink(struct link *plink)
+{
+    switch (plink->type) {
+    case DB_LINK:
+        dbDbScanFwdLink(plink);
+        break;
+    case CA_LINK:
+        dbCaScanFwdLink(plink);
+        break;
+    }
+}
+

=== added file 'src/ioc/db/dbLink.h'
--- src/ioc/db/dbLink.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbLink.h	2012-05-30 18:10:27 +0000
@@ -0,0 +1,94 @@
+/*************************************************************************\
+* Copyright (c) 2010 The UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/*
+ * dbLink.h
+ *
+ *  Created on: Mar 21, 2010
+ *      Author: Andrew Johnson
+ */
+
+#ifndef INC_dbLink_H
+#define INC_dbLink_H
+
+#include "link.h"
+#include "shareLib.h"
+#include "epicsTypes.h"
+#include "epicsTime.h"
+
+typedef struct lset {
+    long (*initLink)(struct link *plink, short dbfType);
+    long (*addLink)(struct link *plink, short dbfType);
+    long (*loadLink)(struct link *plink, short dbrType, void *pbuffer);
+    void (*removeLink)(struct link *plink);
+    int (*isLinkConnected)(const struct link *plink);
+    int (*getDBFtype)(const struct link *plink);
+    long (*getElements)(const struct link *plink, long *nelements);
+    long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
+            epicsEnum16 *pstat, epicsEnum16 *psevr,	long *pnRequest);
+    long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
+    long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
+    long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
+            double *hi, double *hihi);
+    long (*getPrecision)(const struct link *plink, short *precision);
+    long (*getUnits)(const struct link *plink, char *units, int unitsSize);
+    long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
+            epicsEnum16 *severity);
+    long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
+    long (*putValue)(struct link *plink, short dbrType,
+            const void *pbuffer, long nRequest);
+    void (*scanFwdLink)(struct link *plink);
+} lset;
+
+#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \
+    ( ( ( (PLNK)->type == CONSTANT ) && \
+        ( (NREQUEST) == 0) && \
+        ( (OPTIONS) == 0) ) \
+      ? 0 \
+      : dbGetLinkValue((PLNK),(DBRTYPE), \
+        (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) )
+
+#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \
+    ( ( (PLNK)->type == CONSTANT) \
+      ? 0 \
+      : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) )
+
+#define dbGetSevr(PLINK, PSEVERITY) \
+    dbGetAlarm((PLINK), NULL, (PSEVERITY));
+
+epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
+        short dbfType);
+epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
+        short dbfType);
+epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
+        void *pbuffer);
+epicsShareFunc void dbRemoveLink(struct link *plink);
+epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
+epicsShareFunc int dbIsLinkConnected(const struct link *plink);
+epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
+epicsShareFunc long dbGetLinkValue(struct link *, short dbrType, void *pbuffer,
+        long *options, long *nRequest);
+epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low,
+        double *high);
+epicsShareFunc long dbGetGraphicLimits(const struct link *plink, double *low,
+        double *high);
+epicsShareFunc long dbGetAlarmLimits(const struct link *plink, double *lolo,
+        double *low, double *high, double *hihi);
+epicsShareFunc long dbGetPrecision(const struct link *plink, short *precision);
+epicsShareFunc long dbGetUnits(const struct link *plink, char *units,
+        int unitsSize);
+epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
+        epicsEnum16 *severity);
+epicsShareFunc long dbGetTimeStamp(const struct link *plink,
+        epicsTimeStamp *pstamp);
+epicsShareFunc long dbPutLinkValue(struct link *, short dbrType,
+        const void *pbuffer, long nRequest);
+epicsShareFunc void dbScanFwdLink(struct link *plink);
+
+
+#endif /* INC_dbLink_H */

=== modified file 'src/ioc/db/dbNotify.c'
--- src/ioc/db/dbNotify.c	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbNotify.c	2012-05-30 18:10:27 +0000
@@ -5,15 +5,15 @@
 *     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.
 \*************************************************************************/
-/* dbNotify.c */
-/* base/src/db $Revision-Id$ */
+
 /*
  *      Author: 	Marty Kraimer
- *      Date:           03-30-95
+ *                      Andrew Johnson <[email protected]>
+ *
  * Extracted from dbLink.c
-*/
+ */
 
 #include <stdlib.h>
 #include <stdarg.h>
@@ -36,7 +36,7 @@
 #include "dbCommon.h"
 #define epicsExportSharedSymbols
 #include "callback.h"
-#include "dbAddr.h"
+#include "dbChannel.h"
 #include "dbScan.h"
 #include "dbLock.h"
 #include "callback.h"
@@ -45,7 +45,7 @@
 #include "dbNotify.h"
 #include "epicsTime.h"
 #include "cantProceed.h"
-
+
 /*putNotify.state values */
 typedef enum {
     putNotifyNotActive,
@@ -55,44 +55,44 @@
     putNotifyPutInProgress,
     putNotifyUserCallbackRequested,
     putNotifyUserCallbackActive
-}putNotifyState;
+} putNotifyState;
 
 /*structure attached to ppnr field of each record*/
 typedef struct putNotifyRecord {
-    ellCheckNode  waitNode;
-    ELLLIST  restartList; /*list of putNotifys to restart*/
+    ellCheckNode waitNode;
+    ELLLIST restartList; /*list of putNotifys to restart*/
     dbCommon *precord;
-}putNotifyRecord;
+} putNotifyRecord;
 
 #define MAGIC 0xfedc0123
 typedef struct putNotifyPvt {
-    ELLNODE      node;   /*For free list*/
-    long         magic;
-    short        state;
-    CALLBACK     callback;
-    ELLLIST      waitList; /*list of records for current putNotify*/
-    short        cancelWait;
-    short        userCallbackWait;
+    ELLNODE node; /*For free list*/
+    long magic;
+    short state;
+    CALLBACK callback;
+    ELLLIST waitList; /*list of records for current putNotify*/
+    short cancelWait;
+    short userCallbackWait;
     epicsEventId cancelEvent;
     epicsEventId userCallbackEvent;
-}putNotifyPvt;
+} putNotifyPvt;
 
 /* putNotify groups can span locksets if links are dynamically modified*/
 /* Thus a global lock is taken while putNotify fields are accessed     */
 typedef struct notifyGlobal {
     epicsMutexId lock;
     ELLLIST      freeList;
-}notifyGlobal;
+} notifyGlobal;
 
 static notifyGlobal *pnotifyGlobal = 0;
-
+
 /*Local routines*/
 static void putNotifyInit(putNotify *ppn);
 static void putNotifyCleanup(putNotify *ppn);
 static void restartCheck(putNotifyRecord *ppnr);
-static void callUser(dbCommon *precord,putNotify *ppn);
+static void callUser(dbCommon *precord, putNotify *ppn);
 static void notifyCallback(CALLBACK *pcallback);
-static void putNotifyCommon(putNotify *ppn,dbCommon *precord);
+static void putNotifyCommon(putNotify *ppn, dbCommon *precord);
 
 #define ellSafeAdd(list,listnode) \
 { \
@@ -112,9 +112,9 @@
 {
     putNotifyPvt *pputNotifyPvt;
 
-    pputNotifyPvt = (putNotifyPvt *)ellFirst(&pnotifyGlobal->freeList);
-    if(pputNotifyPvt) {
-        ellDelete(&pnotifyGlobal->freeList,&pputNotifyPvt->node);
+    pputNotifyPvt = (putNotifyPvt *) ellFirst(&pnotifyGlobal->freeList);
+    if (pputNotifyPvt) {
+        ellDelete(&pnotifyGlobal->freeList, &pputNotifyPvt->node);
     } else {
         pputNotifyPvt = dbCalloc(1,sizeof(putNotifyPvt));
         pputNotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty);
@@ -135,26 +135,26 @@
 
 static void putNotifyCleanup(putNotify *ppn)
 {
-    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
+    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
 
     pputNotifyPvt->state = putNotifyNotActive;
-    ellAdd(&pnotifyGlobal->freeList,&pputNotifyPvt->node);
+    ellAdd(&pnotifyGlobal->freeList, &pputNotifyPvt->node);
     ppn->pputNotifyPvt = 0;
 }
-
+
 static void restartCheck(putNotifyRecord *ppnr)
 {
     dbCommon *precord = ppnr->precord;
     putNotify *pfirst;
     putNotifyPvt *pputNotifyPvt;
-    
+
     assert(precord->ppn);
-    pfirst = (putNotify *)ellFirst(&ppnr->restartList);
-    if(!pfirst) {
+    pfirst = (putNotify *) ellFirst(&ppnr->restartList);
+    if (!pfirst) {
         precord->ppn = 0;
         return;
     }
-    pputNotifyPvt = (putNotifyPvt *)pfirst->pputNotifyPvt;
+    pputNotifyPvt = (putNotifyPvt *) pfirst->pputNotifyPvt;
     assert(pputNotifyPvt->state==putNotifyWaitForRestart);
     /* remove pfirst from restartList */
     ellSafeDelete(&ppnr->restartList,&pfirst->restartNode);
@@ -165,25 +165,25 @@
     callbackRequest(&pputNotifyPvt->callback);
 }
 
-static void callUser(dbCommon *precord,putNotify *ppn)
+static void callUser(dbCommon *precord, putNotify *ppn)
 {
-    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
+    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
 
     epicsMutexUnlock(pnotifyGlobal->lock);
     dbScanUnlock(precord);
     (*ppn->userCallback)(ppn);
     epicsMutexMustLock(pnotifyGlobal->lock);
-    if(pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) {
+    if (pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) {
         errlogPrintf("%s putNotify: both cancelWait and userCallbackWait true."
-               "This is illegal\n",precord->name);
+            "This is illegal\n", precord->name);
         pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0;
     }
-    if(!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) {
+    if (!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) {
         putNotifyCleanup(ppn);
-        epicsMutexUnlock(pnotifyGlobal->lock); 
+        epicsMutexUnlock(pnotifyGlobal->lock);
         return;
     }
-    if(pputNotifyPvt->cancelWait) {
+    if (pputNotifyPvt->cancelWait) {
         pputNotifyPvt->cancelWait = 0;
         epicsEventSignal(pputNotifyPvt->cancelEvent);
         epicsMutexUnlock(pnotifyGlobal->lock);
@@ -195,38 +195,37 @@
     epicsMutexUnlock(pnotifyGlobal->lock);
     return;
 }
-
-static void putNotifyCommon(putNotify *ppn,dbCommon *precord)
+
+static void putNotifyCommon(putNotify *ppn, dbCommon *precord)
 {
-    long	status=0;
-    dbFldDes	*pfldDes = ppn->paddr->pfldDes;
-    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
+    long status;
+    putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
 
-    if(precord->ppn && pputNotifyPvt->state!=putNotifyRestartCallbackRequested)
-    { /*another putNotify owns the record */
-        pputNotifyPvt->state = putNotifyWaitForRestart; 
+    if (precord->ppn &&
+        pputNotifyPvt->state != putNotifyRestartCallbackRequested) { /*another putNotify owns the record */
+        pputNotifyPvt->state = putNotifyWaitForRestart;
         ellSafeAdd(&precord->ppnr->restartList,&ppn->restartNode);
         epicsMutexUnlock(pnotifyGlobal->lock);
         dbScanUnlock(precord);
         return;
-    } else if(precord->ppn){
-        assert(precord->ppn==ppn);
+    } else if (precord->ppn) {
+        assert(precord->ppn == ppn);
         assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested);
     }
-    if(precord->pact) {
+    if (precord->pact) {
         precord->ppn = ppn;
         ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode);
-        pputNotifyPvt->state = putNotifyRestartInProgress; 
+        pputNotifyPvt->state = putNotifyRestartInProgress;
         epicsMutexUnlock(pnotifyGlobal->lock);
         dbScanUnlock(precord);
         return;
     }
-    status=dbPut(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest);
-    ppn->status = (status==0) ? putNotifyOK : putNotifyError;
+    status = dbChannelPut(ppn->chan, ppn->dbrType, ppn->pbuffer, ppn->nRequest);
+    ppn->status = (status == 0) ? putNotifyOK : putNotifyError;
     /* Check to see if dbProcess should not be called */
-    if(!status /*dont process if dbPut returned error */
-    &&((ppn->paddr->pfield==(void *)&precord->proc) /*If PROC call dbProcess*/
-       || (pfldDes->process_passive && precord->scan==0))) {
+    if (!status /*dont process if dbPut returned error */
+    && ((dbChannelField(ppn->chan) == (void *) & precord->proc) /*If PROC call dbProcess*/
+    || (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) {
         precord->ppn = ppn;
         ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode);
         pputNotifyPvt->state = putNotifyPutInProgress;
@@ -235,31 +234,31 @@
         dbScanUnlock(precord);
         return;
     }
-    if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) {
+    if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) {
         restartCheck(precord->ppnr);
     }
     pputNotifyPvt->state = putNotifyUserCallbackActive;
     assert(precord->ppn!=ppn);
-    callUser(precord,ppn);
+    callUser(precord, ppn);
 }
-
+
 static void notifyCallback(CALLBACK *pcallback)
 {
-    putNotify	*ppn=NULL;
-    dbCommon	*precord;
+    putNotify *ppn = NULL;
+    dbCommon *precord;
     putNotifyPvt *pputNotifyPvt;
 
     callbackGetUser(ppn,pcallback);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    precord = ppn->paddr->precord;
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    precord = dbChannelRecord(ppn->chan);
     dbScanLock(precord);
     epicsMutexMustLock(pnotifyGlobal->lock);
     assert(precord->ppnr);
     assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested
-          || pputNotifyPvt->state==putNotifyUserCallbackRequested);
+            || pputNotifyPvt->state==putNotifyUserCallbackRequested);
     assert(ellCount(&pputNotifyPvt->waitList)==0);
-    if(pputNotifyPvt->cancelWait) {
-        if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) {
+    if (pputNotifyPvt->cancelWait) {
+        if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) {
             restartCheck(precord->ppnr);
         }
         epicsEventSignal(pputNotifyPvt->cancelEvent);
@@ -267,58 +266,61 @@
         dbScanUnlock(precord);
         return;
     }
-    if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) {
-        putNotifyCommon(ppn,precord);
+    if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) {
+        putNotifyCommon(ppn, precord);
         return;
     }
     /* All done. Clean up and call userCallback */
     pputNotifyPvt->state = putNotifyUserCallbackActive;
     assert(precord->ppn!=ppn);
-    callUser(precord,ppn);
+    callUser(precord, ppn);
 }
 
 void epicsShareAPI dbPutNotifyInit(void)
 {
-    if(pnotifyGlobal) return;
+    if (pnotifyGlobal)
+        return;
     pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));
     pnotifyGlobal->lock = epicsMutexMustCreate();
     ellInit(&pnotifyGlobal->freeList);
 }
-
+
 void epicsShareAPI dbPutNotify(putNotify *ppn)
 {
-    dbCommon	*precord = ppn->paddr->precord;
-    short	dbfType = ppn->paddr->field_type;
-    long	status=0;
+    struct dbChannel *chan = ppn->chan;
+    dbCommon *precord = dbChannelRecord(chan);
+    short dbfType = dbChannelFieldType(chan);
+    long status = 0;
     putNotifyPvt *pputNotifyPvt;
 
     assert(precord);
     /*check for putField disabled*/
-    if(precord->disp) {
-        if((void *)(&precord->disp) != ppn->paddr->pfield) {
-           ppn->status = putNotifyPutDisabled;
-           (*ppn->userCallback)(ppn);
-           return;
+    if (precord->disp) {
+        if (dbChannelField(chan) != (void *) & precord->disp) {
+            ppn->status = putNotifyPutDisabled;
+            (*ppn->userCallback)(ppn);
+            return;
         }
     }
     /* Must handle DBF_XXXLINKs as special case.
-     * Only dbPutField will change link fields. 
+     * Only dbPutField will change link fields.
      * Also the record is not processed as a result
-    */
-    if(dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) {
-	status=dbPutField(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest);
-        ppn->status = (status==0) ? putNotifyOK : putNotifyError;
+     */
+    if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
+        status = dbChannelPutField(ppn->chan, ppn->dbrType, ppn->pbuffer,
+                ppn->nRequest);
+        ppn->status = (status == 0) ? putNotifyOK : putNotifyError;
         (*ppn->userCallback)(ppn);
         return;
     }
     dbScanLock(precord);
     epicsMutexMustLock(pnotifyGlobal->lock);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    if(pputNotifyPvt && (pputNotifyPvt->magic!=MAGIC)) {
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    if (pputNotifyPvt && (pputNotifyPvt->magic != MAGIC)) {
         printf("dbPutNotify:pputNotifyPvt was not initialized\n");
         pputNotifyPvt = 0;
     }
-    if(pputNotifyPvt) {
+    if (pputNotifyPvt) {
         assert(pputNotifyPvt->state==putNotifyUserCallbackActive);
         pputNotifyPvt->userCallbackWait = 1;
         epicsMutexUnlock(pnotifyGlobal->lock);
@@ -328,38 +330,38 @@
         epicsMutexMustLock(pnotifyGlobal->lock);
         putNotifyCleanup(ppn);
     }
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
     assert(!pputNotifyPvt);
     putNotifyInit(ppn);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    if(!precord->ppnr) {/* make sure record has a putNotifyRecord*/
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    if (!precord->ppnr) {/* make sure record has a putNotifyRecord*/
         precord->ppnr = dbCalloc(1,sizeof(putNotifyRecord));
         precord->ppnr->precord = precord;
         ellInit(&precord->ppnr->restartList);
     }
-    putNotifyCommon(ppn,precord);
+    putNotifyCommon(ppn, precord);
 }
-
+
 void epicsShareAPI dbNotifyCancel(putNotify *ppn)
 {
-    dbCommon	*precord = ppn->paddr->precord;
+    dbCommon *precord = dbChannelRecord(ppn->chan);
     putNotifyState state;
     putNotifyPvt *pputNotifyPvt;
 
     assert(precord);
     dbScanLock(precord);
     epicsMutexMustLock(pnotifyGlobal->lock);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    if(!pputNotifyPvt || pputNotifyPvt->state==putNotifyNotActive) {
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    if (!pputNotifyPvt || pputNotifyPvt->state == putNotifyNotActive) {
         epicsMutexUnlock(pnotifyGlobal->lock);
         dbScanUnlock(precord);
         return;
     }
     state = pputNotifyPvt->state;
     /*If callback is scheduled or active wait for it to complete*/
-    if(state==putNotifyUserCallbackRequested
-    || state==putNotifyRestartCallbackRequested
-    || state==putNotifyUserCallbackActive) {
+    if (state == putNotifyUserCallbackRequested || state
+            == putNotifyRestartCallbackRequested || state
+            == putNotifyUserCallbackActive) {
         pputNotifyPvt->cancelWait = 1;
         epicsMutexUnlock(pnotifyGlobal->lock);
         dbScanUnlock(precord);
@@ -369,24 +371,26 @@
         epicsMutexUnlock(pnotifyGlobal->lock);
         return;
     }
-    switch(state) {
-    case putNotifyNotActive: break;
+    switch (state) {
+    case putNotifyNotActive:
+        break;
     case putNotifyWaitForRestart:
         assert(precord->ppn);
         assert(precord->ppn!=ppn);
-        ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode);
+        ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode)
+        ;
         break;
     case putNotifyRestartInProgress:
-    case putNotifyPutInProgress:
-        { /*Take all records out of wait list */
-            putNotifyRecord *ppnrWait;
+    case putNotifyPutInProgress: { /*Take all records out of wait list */
+        putNotifyRecord *ppnrWait;
 
-            while((ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList))){
-                ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode);
-                restartCheck(ppnrWait);
-            }
+        while ((ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList))) {
+            ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode);
+            restartCheck(ppnrWait);
         }
-        if(precord->ppn==ppn) restartCheck(precord->ppnr);
+    }
+        if (precord->ppn == ppn)
+            restartCheck(precord->ppnr);
         break;
     default:
         printf("dbNotify: illegal state for notifyCallback\n");
@@ -396,29 +400,29 @@
     epicsMutexUnlock(pnotifyGlobal->lock);
     dbScanUnlock(precord);
 }
-
+
 void epicsShareAPI dbNotifyCompletion(dbCommon *precord)
 {
-    putNotify	*ppn = precord->ppn;
+    putNotify *ppn = precord->ppn;
     putNotifyPvt *pputNotifyPvt;
 
     epicsMutexMustLock(pnotifyGlobal->lock);
     assert(ppn);
     assert(precord->ppnr);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    if(pputNotifyPvt->state!=putNotifyRestartInProgress
-    && pputNotifyPvt->state!=putNotifyPutInProgress) {
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    if (pputNotifyPvt->state != putNotifyRestartInProgress
+            && pputNotifyPvt->state != putNotifyPutInProgress) {
         epicsMutexUnlock(pnotifyGlobal->lock);
         return;
     }
     ellSafeDelete(&pputNotifyPvt->waitList,&precord->ppnr->waitNode);
-    if((ellCount(&pputNotifyPvt->waitList)!=0)) {
+    if ((ellCount(&pputNotifyPvt->waitList)!=0)) {
         restartCheck(precord->ppnr);
-    } else if(pputNotifyPvt->state == putNotifyPutInProgress) {
+    } else if (pputNotifyPvt->state == putNotifyPutInProgress) {
         pputNotifyPvt->state = putNotifyUserCallbackRequested;
         restartCheck(precord->ppnr);
         callbackRequest(&pputNotifyPvt->callback);
-    } else if(pputNotifyPvt->state == putNotifyRestartInProgress) {
+    } else if (pputNotifyPvt->state == putNotifyRestartInProgress) {
         pputNotifyPvt->state = putNotifyRestartCallbackRequested;
         callbackRequest(&pputNotifyPvt->callback);
     } else {
@@ -432,142 +436,140 @@
     putNotify *ppn = pfrom->ppn;
     putNotifyPvt *pputNotifyPvt;
 
-    if(pto->pact) return; /*if active it will not be processed*/
+    if (pto->pact)
+        return; /*if active it will not be processed*/
     epicsMutexMustLock(pnotifyGlobal->lock);
-    if(!pto->ppnr) {/* make sure record has a putNotifyRecord*/
+    if (!pto->ppnr) {/* make sure record has a putNotifyRecord*/
         pto->ppnr = dbCalloc(1,sizeof(putNotifyRecord));
         pto->ppnr->precord = pto;
         ellInit(&pto->ppnr->restartList);
     }
     assert(ppn);
-    pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-    if(!(pto->ppn) 
-    && (pputNotifyPvt->state==putNotifyPutInProgress)
-    && (pto!=ppn->paddr->precord)) {
+    pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+    if (!pto->ppn &&
+        (pputNotifyPvt->state == putNotifyPutInProgress) &&
+        (pto != dbChannelRecord(ppn->chan))) {
         putNotifyPvt *pputNotifyPvt;
         pto->ppn = pfrom->ppn;
-        pputNotifyPvt = (putNotifyPvt *)pfrom->ppn->pputNotifyPvt;
+        pputNotifyPvt = (putNotifyPvt *) pfrom->ppn->pputNotifyPvt;
         ellSafeAdd(&pputNotifyPvt->waitList,&pto->ppnr->waitNode);
     }
     epicsMutexUnlock(pnotifyGlobal->lock);
 }
-
+
 typedef struct tpnInfo {
     epicsEventId callbackDone;
-    putNotify    *ppn;
-}tpnInfo;
+    putNotify *ppn;
+} tpnInfo;
 
 static void dbtpnCallback(putNotify *ppn)
 {
     putNotifyStatus status = ppn->status;
-    tpnInfo         *ptpnInfo = (tpnInfo *)ppn->usrPvt;
+    tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt;
+    const char *pname = dbChannelRecord(ppn->chan)->name;
 
-    if(status==0)
-	printf("dbtpnCallback: success record=%s\n",ppn->paddr->precord->name);
+    if (status == 0)
+        printf("dbtpnCallback: success record=%s\n", pname);
     else
-        printf("%s dbtpnCallback putNotify.status %d\n",ppn->paddr->precord->name,(int)status);
+        printf("%s dbtpnCallback putNotify.status %d\n",
+                pname, (int) status);
     epicsEventSignal(ptpnInfo->callbackDone);
 }
 
 static void tpnThread(void *pvt)
 {
-    tpnInfo   *ptpnInfo = (tpnInfo *)pvt;
-    putNotify *ppn = (putNotify *)ptpnInfo->ppn;
+    tpnInfo *ptpnInfo = (tpnInfo *) pvt;
+    putNotify *ppn = (putNotify *) ptpnInfo->ppn;
 
     dbPutNotify(ppn);
     epicsEventWait(ptpnInfo->callbackDone);
     dbNotifyCancel(ppn);
     epicsEventDestroy(ptpnInfo->callbackDone);
-    free((void *)ppn->paddr);
+    free(ppn->pbuffer);
+    dbChannelDelete(ppn->chan);
     free(ppn);
     free(ptpnInfo);
 }
 
-long epicsShareAPI dbtpn(char	*pname,char *pvalue)
+long epicsShareAPI dbtpn(char *pname, char *pvalue)
 {
-    long	status;
-    tpnInfo     *ptpnInfo;
-    DBADDR	*pdbaddr=NULL;
-    putNotify	*ppn=NULL;
-    char	*psavevalue;
-    int		len;
+    struct dbChannel *chan;
+    tpnInfo *ptpnInfo;
+    putNotify *ppn;
+    char *pbuffer;
 
-    len = strlen(pvalue);
-    /*allocate space for value immediately following DBADDR*/
-    pdbaddr = dbCalloc(1,sizeof(DBADDR) + len+1);
-    psavevalue = (char *)(pdbaddr + 1);
-    strcpy(psavevalue,pvalue);
-    status = dbNameToAddr(pname,pdbaddr);
-    if(status) {
-	errMessage(status, "dbtpn: dbNameToAddr");
-	free((void *)pdbaddr);
-	return(-1);
+    pbuffer = strdup(pvalue);
+    chan = dbChannelCreate(pname);
+    if (!chan) {
+        printf("dbtpn: No such channel");
+        return -1;
     }
-    ppn = dbCalloc(1,sizeof(putNotify));
-    ppn->paddr = pdbaddr;
-    ppn->pbuffer = psavevalue;
+
+    ppn = dbCalloc(1, sizeof(putNotify));
+    ppn->chan = chan;
+    ppn->pbuffer = pbuffer;
     ppn->nRequest = 1;
     ppn->dbrType = DBR_STRING;
     ppn->userCallback = dbtpnCallback;
-    ptpnInfo = dbCalloc(1,sizeof(tpnInfo));
+    ptpnInfo = dbCalloc(1, sizeof(tpnInfo));
     ptpnInfo->ppn = ppn;
     ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty);
     ppn->usrPvt = ptpnInfo;
-    epicsThreadCreate("dbtpn",epicsThreadPriorityHigh,
-        epicsThreadGetStackSize(epicsThreadStackMedium),
-        tpnThread,ptpnInfo);
-    return(0);
+    epicsThreadCreate("dbtpn", epicsThreadPriorityHigh,
+    epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo);
+    return 0;
 }
-
+
 int epicsShareAPI dbNotifyDump(void)
 {
     epicsMutexLockStatus lockStatus;
     dbRecordType *pdbRecordType;
     dbRecordNode *pdbRecordNode;
-    dbCommon     *precord;
-    putNotify    *ppn;
-    putNotify    *ppnRestart;
+    dbCommon *precord;
+    putNotify *ppn;
+    putNotify *ppnRestart;
     putNotifyRecord *ppnrWait;
     int itry;
-   
-    
-    for(itry=0; itry<100; itry++) {
+
+    for (itry = 0; itry < 100; itry++) {
         lockStatus = epicsMutexTryLock(pnotifyGlobal->lock);
-        if(lockStatus==epicsMutexLockOK) break;
+        if (lockStatus == epicsMutexLockOK)
+            break;
         epicsThreadSleep(.05);
     }
-    for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
-    pdbRecordType;
-    pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
-        for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList);
-        pdbRecordNode;
-        pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {
+    for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType
+            = (dbRecordType *) ellNext(&pdbRecordType->node)) {
+        for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode
+                = (dbRecordNode *) ellNext(&pdbRecordNode->node)) {
             putNotifyPvt *pputNotifyPvt;
             precord = pdbRecordNode->precord;
-            if (!precord->name[0] ||
-                pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
-                continue;
-            if(!precord->ppn) continue;
-            if(!precord->ppnr) continue;
-            if(precord->ppn->paddr->precord != precord) continue;
+            if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
+                continue;
+            if (!precord->ppn)
+                continue;
+            if (!precord->ppnr)
+                continue;
+            if (dbChannelRecord(precord->ppn->chan) != precord)
+                continue;
             ppn = precord->ppn;
-            pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt;
-            printf("%s state %d ppn %p\n  waitList\n",
-                precord->name,pputNotifyPvt->state,(void*)ppn);
-            ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList);
-            while(ppnrWait) {
-                printf("    %s pact %d\n",
-                    ppnrWait->precord->name,ppnrWait->precord->pact);
-                ppnrWait = (putNotifyRecord *)ellNext(&ppnrWait->waitNode.node);
+            pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt;
+            printf("%s state %d ppn %p\n  waitList\n", precord->name,
+                    pputNotifyPvt->state, (void*) ppn);
+            ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList);
+            while (ppnrWait) {
+                printf("    %s pact %d\n", ppnrWait->precord->name,
+                        ppnrWait->precord->pact);
+                ppnrWait = (putNotifyRecord *) ellNext(&ppnrWait->waitNode.node);
             }
             printf("  restartList\n");
-            ppnRestart = (putNotify *)ellFirst(&precord->ppnr->restartList);
-            while(ppnRestart) {
-                printf("    %p\n", (void *)ppnRestart);
-                ppnRestart = (putNotify *)ellNext(&ppnRestart->restartNode.node);
+            ppnRestart = (putNotify *) ellFirst(&precord->ppnr->restartList);
+            while (ppnRestart) {
+                printf("    %p\n", (void *) ppnRestart);
+                ppnRestart = (putNotify *) ellNext(&ppnRestart->restartNode.node);
             }
         }
     }
-    if(lockStatus==epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock);
-    return(0);
+    if (lockStatus == epicsMutexLockOK)
+        epicsMutexUnlock(pnotifyGlobal->lock);
+    return (0);
 }

=== modified file 'src/ioc/db/dbNotify.h'
--- src/ioc/db/dbNotify.h	2006-11-18 00:09:27 +0000
+++ src/ioc/db/dbNotify.h	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 /* dbNotify.h	*/
 
@@ -18,12 +18,12 @@
 #include "callback.h"
 
 #ifdef __cplusplus
-	/* for brain dead C++ compilers */
-	struct dbCommon;
-	struct putNotify;
     extern "C" {
 #endif
-
+
+struct dbCommon;
+struct putNotify;
+
 typedef struct ellCheckNode{
     ELLNODE node;
     int     isOnList;
@@ -40,7 +40,7 @@
         ellCheckNode    restartNode;
         /*The following members MUST be set by user*/
         void            (*userCallback)(struct putNotify *);
-        struct dbAddr   *paddr;         /*dbAddr set by dbNameToAddr*/
+        struct dbChannel *chan;         /*dbChannel*/
         void            *pbuffer;       /*address of data*/
         long            nRequest;       /*number of elements to be written*/
         short           dbrType;        /*database request type*/
@@ -55,7 +55,7 @@
 epicsShareFunc void epicsShareAPI dbPutNotify(putNotify *pputNotify);
 epicsShareFunc void epicsShareAPI dbNotifyCancel(putNotify *pputNotify);
 
-/*dbPutNotifyMapType convience function for old database access*/
+/*dbPutNotifyMapType convenience function for old database access*/
 epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype);
 
 /* dbPutNotifyInit called by iocInit */
@@ -70,9 +70,9 @@
 /* dbtpn is test routine for put notify */
 epicsShareFunc long epicsShareAPI dbtpn(char *recordname,char *value);
 
-/* dbNotifyDump is an INVASIVE debug utility. Dont use this needlessly*/
+/* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/
 epicsShareFunc int epicsShareAPI dbNotifyDump(void);
-
+
 /* This module provides code to handle put notify. If a put causes a record to
  * be processed, then a user supplied callback is called when that record
  * and all records processed because of that record complete processing.
@@ -87,8 +87,8 @@
  *
  * After dbPutNotify is called it may not called for the same putNotify
  * until the putCallback is complete. The use can call dbNotifyCancel
- * to cancel the operation. 
- *    
+ * to cancel the operation.
+ *
  * The user callback is called when the operation is completed.
  *
  * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by:

=== modified file 'src/ioc/db/dbPutNotifyBlocker.cpp'
--- src/ioc/db/dbPutNotifyBlocker.cpp	2011-10-19 18:07:00 +0000
+++ src/ioc/db/dbPutNotifyBlocker.cpp	2012-05-30 18:10:27 +0000
@@ -5,21 +5,21 @@
 *     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.
 \*************************************************************************/
 
-/*  
+/*
  *  $Revision-Id$
  *
- *                              
+ *
  *                    L O S  A L A M O S
  *              Los Alamos National Laboratory
  *               Los Alamos, New Mexico 87545
- *                                  
+ *
  *  Copyright, 1986, The Regents of the University of California.
- *                                  
- *           
- *  Author: 
+ *
+ *
+ *  Author:
  *  Jeffrey O. Hill
  *  [email protected]
  *  505 665 1831
@@ -47,7 +47,7 @@
 #include "dbPutNotifyBlocker.h"
 
 dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) :
-    mutex ( mutexIn ), pNotify ( 0 ), 
+    mutex ( mutexIn ), pNotify ( 0 ),
     maxValueSize ( sizeof ( this->dbrScalarValue ) )
 {
     memset ( & this->pn, '\0', sizeof ( this->pn ) );
@@ -55,7 +55,7 @@
     this->pn.pbuffer = & this->dbrScalarValue;
 }
 
-dbPutNotifyBlocker::~dbPutNotifyBlocker () 
+dbPutNotifyBlocker::~dbPutNotifyBlocker ()
 {
 }
 
@@ -70,7 +70,7 @@
     this->~dbPutNotifyBlocker ();
 }
 
-void dbPutNotifyBlocker::cancel ( 
+void dbPutNotifyBlocker::cancel (
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -82,7 +82,7 @@
     this->block.signal ();
 }
 
-void dbPutNotifyBlocker::expandValueBuf ( 
+void dbPutNotifyBlocker::expandValueBuf (
     epicsGuard < epicsMutex > & guard, unsigned long newSize )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -127,9 +127,9 @@
     }
 }
 
-void dbPutNotifyBlocker::initiatePutNotify ( 
-    epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, 
-    struct dbAddr & addr, unsigned type, unsigned long count, 
+void dbPutNotifyBlocker::initiatePutNotify (
+    epicsGuard < epicsMutex > & guard, cacWriteNotify & notify,
+    struct dbChannel * dbch, unsigned type, unsigned long count,
     const void * pValue )
 {
     guard. assertIdenticalMutex ( this->mutex );
@@ -163,7 +163,7 @@
         throw cacChannel::badType();
     }
 
-    int status = dbPutNotifyMapType ( 
+    int status = dbPutNotifyMapType (
                 &this->pn, static_cast <short> ( type ) );
     if ( status ) {
         this->pNotify = 0;
@@ -171,7 +171,7 @@
     }
 
     this->pn.nRequest = static_cast < unsigned > ( count );
-    this->pn.paddr = &addr;
+    this->pn.chan = dbch;
     this->pn.userCallback = putNotifyCompletion;
     this->pn.usrPvt = this;
 
@@ -191,22 +191,22 @@
     this->show ( guard, level );
 }
 
-void dbPutNotifyBlocker::show ( 
+void dbPutNotifyBlocker::show (
     epicsGuard < epicsMutex > &, unsigned level ) const
 {
-    printf ( "put notify blocker at %p\n", 
+    printf ( "put notify blocker at %p\n",
         static_cast <const void *> ( this ) );
     if ( level > 0u ) {
         this->block.show ( level - 1u );
     }
 }
 
-dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () 
+dbSubscriptionIO * dbPutNotifyBlocker::isSubscription ()
 {
     return 0;
 }
 
-void * dbPutNotifyBlocker::operator new ( size_t size, 
+void * dbPutNotifyBlocker::operator new ( size_t size,
     tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList )
 {
     return freeList.allocate ( size );
@@ -220,7 +220,7 @@
 }
 
 #ifdef CXX_PLACEMENT_DELETE
-void dbPutNotifyBlocker::operator delete ( void *pCadaver, 
+void dbPutNotifyBlocker::operator delete ( void *pCadaver,
     tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList )
 {
     freeList.release ( pCadaver );

=== modified file 'src/ioc/db/dbPutNotifyBlocker.h'
--- src/ioc/db/dbPutNotifyBlocker.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbPutNotifyBlocker.h	2012-05-30 18:10:27 +0000
@@ -5,20 +5,20 @@
 *     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.
 \*************************************************************************/
 
-/*  
+/*
  *  $Revision-Id$
  *
- *                              
+ *
  *                    L O S  A L A M O S
  *              Los Alamos National Laboratory
  *               Los Alamos, New Mexico 87545
- *                                  
+ *
  *  Copyright, 1986, The Regents of the University of California.
- *                                  
- *           
+ *
+ *
  *	Author Jeffrey O. Hill
  *	[email protected]
  *	505 665 1831
@@ -43,24 +43,24 @@
 public:
     dbPutNotifyBlocker ( epicsMutex & );
     void destructor ( epicsGuard < epicsMutex > & );
-    void initiatePutNotify ( epicsGuard < epicsMutex > &, 
-            cacWriteNotify &, struct dbAddr &, 
+    void initiatePutNotify ( epicsGuard < epicsMutex > &,
+            cacWriteNotify &, struct dbChannel *,
             unsigned type, unsigned long count, const void * pValue );
     void cancel ( epicsGuard < epicsMutex > & );
     void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
     void show ( unsigned level ) const;
-    void * operator new ( size_t size, 
+    void * operator new ( size_t size,
         tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & );
-    epicsPlacementDeleteOperator (( void *, 
+    epicsPlacementDeleteOperator (( void *,
         tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ))
 private:
     putNotify pn;
     //
-    // Include a union of all scalar types 
+    // Include a union of all scalar types
     // including fixed length strings so
-    // that in many cases we can avoid 
+    // that in many cases we can avoid
     // allocating another buffer
-    // 
+    //
     union {
         dbr_string_t strval;
         dbr_short_t shrtval;
@@ -76,7 +76,7 @@
     cacWriteNotify * pNotify;
     unsigned long maxValueSize;
     dbSubscriptionIO * isSubscription ();
-    void expandValueBuf ( 
+    void expandValueBuf (
         epicsGuard < epicsMutex > &, unsigned long newSize );
     friend void putNotifyCompletion ( putNotify * ppn );
 	dbPutNotifyBlocker ( const dbPutNotifyBlocker & );

=== added file 'src/ioc/db/dbState.c'
--- src/ioc/db/dbState.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbState.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,107 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "iocsh.h"
+#include "epicsMutex.h"
+#include "ellLib.h"
+#include "dbStaticLib.h"
+#include "dbDefs.h"
+
+#include "dbState.h"
+
+static ELLLIST states = ELLLIST_INIT;
+
+typedef struct dbState {
+    ELLNODE node;
+    int status;
+    char *name;
+    epicsMutexId lock;        /* FIXME: Use atomic operations instead */
+} dbState;
+
+dbStateId dbStateFind(const char *name)
+{
+    ELLNODE *node;
+    dbStateId id;
+
+    for (node = ellFirst(&states); node; node = ellNext(node)) {
+        id = CONTAINER(node, dbState, node);
+        if (strcmp(id->name, name) == 0)
+            return id;
+    }
+    return NULL;
+}
+
+dbStateId dbStateCreate(const char *name)
+{
+    dbStateId id;
+
+    if ((id = dbStateFind(name)))
+        return id;
+
+    id = callocMustSucceed(1, sizeof(dbState), "createDbState");
+    id->name = strdup(name);
+    id->lock = epicsMutexMustCreate();
+    ellAdd(&states, &id->node);
+
+    return id;
+}
+
+void dbStateSet(dbStateId id)
+{
+    if (!id)
+        return;
+    epicsMutexMustLock(id->lock);
+    id->status = 1;
+    epicsMutexUnlock(id->lock);
+}
+
+void dbStateClear(dbStateId id)
+{
+    if (!id)
+        return;
+    epicsMutexMustLock(id->lock);
+    id->status = 0;
+    epicsMutexUnlock(id->lock);
+}
+
+int dbStateGet(dbStateId id)
+{
+    int status;
+
+    if (!id)
+        return 0;
+    epicsMutexMustLock(id->lock);
+    status = id->status;
+    epicsMutexUnlock(id->lock);
+    return status;
+}
+
+void dbStateShow(dbStateId id, unsigned int level)
+{
+    if (level >=1)
+        printf("id %#x '%s' : ", (unsigned int) id, id->name);
+    printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE");
+}
+
+void dbStateShowAll(unsigned int level)
+{
+    ELLNODE *node;
+    dbStateId id;
+
+    for (node = ellFirst(&states); node; node = ellNext(node)) {
+        id = CONTAINER(node, dbState, node);
+        dbStateShow(id, level+1);
+    }
+}

=== added file 'src/ioc/db/dbState.h'
--- src/ioc/db/dbState.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbState.h	2012-05-30 18:10:27 +0000
@@ -0,0 +1,92 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#ifndef INCdbStateH
+#define INCdbStateH
+
+#include "shareLib.h"
+
+/** @file dbState.h
+ * @brief Generic IOC state facility
+ *
+ * This library provides a simple global flag facility that can be used to
+ * synchronize e.g. plugins with IOC-wide states, that may be derived from
+ * events (either soft events or hard events coming from specialized timing
+ * and event hardware).
+ *
+ * A subset of this API is provided as IOC Shell commands to allow
+ * command line debugging.
+ *
+ */
+
+typedef struct dbState *dbStateId;
+
+/** @brief Create db state.
+ *
+ * Creates a new db state with the specified 'name', returning the new id.
+ * If state with that name already exists, the existing state's id is returned.
+ *
+ * <em>Also provided as an IOC Shell command.</em>
+ *
+ * @param name Db state name.
+ * @return Id of db state, NULL for failure.
+ */
+epicsShareFunc dbStateId dbStateCreate(const char *name);
+
+/** @brief Find db state.
+ *
+ * @param name Db state name.
+ * @return Id of db state, NULL if not found.
+ */
+epicsShareFunc dbStateId dbStateFind(const char *name);
+
+/** @brief Set db state to TRUE.
+ *
+ * <em>Also provided as an IOC Shell command.</em>
+ *
+ * @param id Db state id.
+ */
+epicsShareFunc void dbStateSet(dbStateId id);
+
+/** @brief Set db state to FALSE.
+ *
+ * <em>Also provided as an IOC Shell command.</em>
+ *
+ * @param id Db state id.
+ */
+epicsShareFunc void dbStateClear(dbStateId id);
+
+/** @brief Get db state.
+ *
+ * @param id Db state id.
+ * @return Current db state (0|1).
+ */
+epicsShareFunc int dbStateGet(dbStateId id);
+
+/** @brief Print info about db state.
+ *
+ * <em>Also provided as an IOC Shell command.</em>
+ *
+ * @param id Db state id.
+ * @param level Interest level.
+ */
+epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
+
+/** @brief Print info about all db states.
+ *
+ * <em>Also provided as an IOC Shell command.</em>
+ *
+ * @param level Interest level.
+ */
+epicsShareFunc void dbStateShowAll(unsigned int level);
+
+#endif // INCdbStateH

=== modified file 'src/ioc/db/dbSubscriptionIO.cpp'
--- src/ioc/db/dbSubscriptionIO.cpp	2010-10-05 19:27:37 +0000
+++ src/ioc/db/dbSubscriptionIO.cpp	2012-05-30 18:10:27 +0000
@@ -4,20 +4,20 @@
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of 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.
 \*************************************************************************/
 
-/*  
+/*
  *  $Revision-Id$
  *
- *                              
+ *
  *                    L O S  A L A M O S
  *              Los Alamos National Laboratory
  *               Los Alamos, New Mexico 87545
- *                                  
+ *
  *  Copyright, 1986, The Regents of the University of California.
- *                                  
- *           
+ *
+ *
  *	Author Jeffrey O. Hill
  *	[email protected]
  *	505 665 1831
@@ -41,18 +41,18 @@
 #include "dbChannelIO.h"
 #include "db_access_routines.h"
 
-dbSubscriptionIO::dbSubscriptionIO ( 
+dbSubscriptionIO::dbSubscriptionIO (
     epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn,
-    dbContext &, dbChannelIO & chanIO, 
-    dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, 
+    dbContext &, dbChannelIO & chanIO,
+    dbChannel * dbch, cacStateNotify & notifyIn, unsigned typeIn,
     unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) :
-    mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), 
+    mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ),
     chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u )
 {
     guard.assertIdenticalMutex ( this->mutex );
     {
         epicsGuardRelease < epicsMutex > unguard ( guard );
-        this->es = db_add_event ( ctx, & addr,
+        this->es = db_add_event ( ctx, dbch,
             dbSubscriptionEventCallback, (void *) this, maskIn );
         if ( this->es == 0 ) {
             throw std::bad_alloc();
@@ -62,7 +62,7 @@
     }
 }
 
-dbSubscriptionIO::~dbSubscriptionIO () 
+dbSubscriptionIO::~dbSubscriptionIO ()
 {
 }
 
@@ -72,7 +72,7 @@
     this->~dbSubscriptionIO ();
 }
 
-void dbSubscriptionIO::unsubscribe ( 
+void dbSubscriptionIO::unsubscribe (
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );
@@ -86,11 +86,11 @@
     }
 }
 
-void dbSubscriptionIO::channelDeleteException ( 
+void dbSubscriptionIO::channelDeleteException (
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );
-    this->notify.exception ( guard, ECA_CHANDESTROY, 
+    this->notify.exception ( guard, ECA_CHANDESTROY,
         this->chan.pName(guard), this->type, this->count );
 }
 
@@ -112,21 +112,21 @@
         __FILE__, __LINE__ );
 }
 
-void * dbSubscriptionIO::operator new ( size_t size, 
+void * dbSubscriptionIO::operator new ( size_t size,
         tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList )
 {
     return freeList.allocate ( size );
 }
 
 #ifdef CXX_PLACEMENT_DELETE
-void dbSubscriptionIO::operator delete ( void * pCadaver, 
+void dbSubscriptionIO::operator delete ( void * pCadaver,
         tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList )
 {
     freeList.release ( pCadaver );
 }
 #endif
 
-extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr * /* paddr */,
+extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel * /* dbch */,
 	int /* eventsRemaining */, struct db_field_log *pfl )
 {
     dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate );
@@ -139,19 +139,19 @@
     this->show ( guard, level );
 }
 
-void dbSubscriptionIO::show ( 
+void dbSubscriptionIO::show (
     epicsGuard < epicsMutex > & guard, unsigned level ) const
 {
     guard.assertIdenticalMutex ( this->mutex );
 
-    printf ( "Data base subscription IO at %p\n", 
+    printf ( "Data base subscription IO at %p\n",
         static_cast <const void *> ( this ) );
     if ( level > 0u ) {
         short tmpType;
         if ( this->type < SHRT_MAX ) {
             tmpType = static_cast < short > ( this->type );
             printf ( "\ttype %s, count %lu, channel at %p\n",
-                dbf_type_to_text ( tmpType ), this->count, 
+                dbf_type_to_text ( tmpType ), this->count,
                 static_cast <void *> ( &this->chan ) );
         }
         else {
@@ -161,7 +161,7 @@
     }
 }
 
-dbSubscriptionIO * dbSubscriptionIO::isSubscription () 
+dbSubscriptionIO * dbSubscriptionIO::isSubscription ()
 {
     return this;
 }

=== modified file 'src/ioc/db/db_access.c'
--- src/ioc/db/db_access.c	2011-08-30 23:09:11 +0000
+++ src/ioc/db/db_access.c	2012-05-30 18:10:27 +0000
@@ -4,15 +4,13 @@
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of 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.
 \*************************************************************************/
 
-/* $Revision-Id$ */
-
 /* Interface between old database access and new
  *
  *      Author:          Marty Kraimer
- *      Date:            6-1-90
+ *                       Andrew Johnson <[email protected]>
  */
 
 #include <stddef.h>
@@ -24,6 +22,7 @@
 
 #include "epicsConvert.h"
 #include "dbDefs.h"
+#include "dbChannel.h"
 #include "errlog.h"
 #include "ellLib.h"
 #include "epicsTime.h"
@@ -42,10 +41,6 @@
 #include "dbEvent.h"
 #include "db_access_routines.h"
 
-#ifndef NULL
-#define NULL 0
-#endif
-
 
 #define oldDBF_STRING      0
 #define oldDBF_INT         1
@@ -108,50 +103,36 @@
 typedef char DBSTRING[MAX_STRING_SIZE];
 
 
-#ifndef MAX_STRING_SIZE
-#define MAX_STRING_SIZE	40
-#endif
-
-/*
- * DB_PROCESS
- *
- * process database records
- */
-void db_process(struct dbAddr *paddr)
-{
-    long status = dbProcess(paddr->precord);
-
-    if (status) errMessage(status, "db_process failed");
-}
-
-/*
- * DB_NAME_TO_ADDR
- */
-int epicsShareAPI db_name_to_addr(const char *pname, struct dbAddr *paddr)
-{
-    long status;
+struct dbChannel * dbChannel_create(const char *pname)
+{
+    dbChannel *chan = dbChannelCreate(pname);
     short ftype;
 
-    status = dbNameToAddr(pname, paddr);
-    if (!status) {
-        ftype = paddr->dbr_field_type;
-        if (INVALID_DB_REQ(ftype)) {
-            errlogPrintf("%s dbNameToAddr failed\n", pname);
-            return -2;
-        }
-        paddr->dbr_field_type = dbDBRnewToDBRold[ftype];
-        return 0;
-    }
-    else
-        return -1;
+    if (!chan)
+        return NULL;
+
+    ftype = chan->addr.dbr_field_type;
+    if (INVALID_DB_REQ(ftype)) {
+        dbChannelDelete(chan);
+        return NULL;
+    }
+
+    chan->addr.dbr_field_type = dbDBRnewToDBRold[ftype];
+
+    if (dbChannelOpen(chan)) {
+        dbChannelDelete(chan);
+        return NULL;
+    }
+
+    return chan;
 }
-
-int epicsShareAPI db_get_field(struct dbAddr *paddr,
-    int buffer_type, void *pbuffer, int no_elements, void *pfl)
+
+int dbChannel_get(struct dbChannel *chan,
+    int buffer_type, void *pbuffer, long no_elements, void *pfl)
 {
     long nRequest = no_elements;
-    int result = db_get_field_and_count(
-        paddr, buffer_type, pbuffer, &nRequest, pfl);
+    int result = dbChannel_get_count(
+        chan, buffer_type, pbuffer, &nRequest, pfl);
     if (nRequest < no_elements) {
         /* The database request returned fewer elements than requested, so
          * fill the remainder of the buffer with zeros.
@@ -168,10 +149,11 @@
 /* Performs the work of the public db_get_field API, but also returns the number
  * of elements actually copied to the buffer.  The caller is responsible for
  * zeroing the remaining part of the buffer. */
-int epicsShareAPI db_get_field_and_count(
-    struct dbAddr *paddr, int buffer_type,
+int epicsShareAPI dbChannel_get_count(
+    struct dbChannel *chan, int buffer_type,
     void *pbuffer, long *nRequest, void *pfl)
 {
+    dbAddr *paddr=&chan->addr;
     long status;
     long options;
     long i;
@@ -184,26 +166,26 @@
 
     switch(buffer_type) {
     case(oldDBR_STRING):
-        status = dbGetField(paddr, DBR_STRING, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
         break;
 /*  case(oldDBR_INT): */
     case(oldDBR_SHORT):
-        status = dbGetField(paddr, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
         break;
     case(oldDBR_FLOAT):
-        status = dbGetField(paddr, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
         break;
     case(oldDBR_ENUM):
-        status = dbGetField(paddr, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
         break;
     case(oldDBR_CHAR):
-        status = dbGetField(paddr, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
         break;
     case(oldDBR_LONG):
-        status = dbGetField(paddr, DBR_LONG, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
         break;
     case(oldDBR_DOUBLE):
-        status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
+        status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
         break;
 
     case(oldDBR_STS_STRING):
@@ -216,10 +198,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_STRING, pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -232,10 +214,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_SHORT, &pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -247,10 +229,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_FLOAT, &pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -262,10 +244,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_ENUM, &pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -277,10 +259,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_UCHAR, &pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -292,10 +274,10 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
-            status = dbGetField(paddr, DBR_LONG, &pold->value, &zero,
+            status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero,
                 nRequest, pfl);
         }
         break;
@@ -307,11 +289,11 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             options = 0;
-            status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -325,12 +307,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_STRING, pold->value, &options,
+            status = dbChannelGetField(chan, DBR_STRING, pold->value, &options,
                     nRequest, pfl);
         }
         break;
@@ -344,12 +326,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -362,12 +344,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -380,12 +362,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_ENUM, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -398,12 +380,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_CHAR, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -416,12 +398,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -434,12 +416,12 @@
             } newSt;
 
             options = DBR_STATUS | DBR_TIME;
-            status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->stamp = newSt.time;         /* structure copy */
             options = 0;
-            status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -457,7 +439,7 @@
             } newSt;
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -469,7 +451,7 @@
             pold->lower_warning_limit = newSt.lower_warning_limit;
             pold->lower_alarm_limit = newSt.lower_alarm_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -486,7 +468,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
                 DBR_AL_DOUBLE;
-            status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->precision = newSt.precision.dp;
@@ -499,7 +481,7 @@
             pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit);
             pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit);
             options = 0;
-            status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -515,7 +497,7 @@
             } newSt;
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -527,7 +509,7 @@
             pold->lower_warning_limit = newSt.lower_warning_limit;
             pold->lower_alarm_limit = newSt.lower_alarm_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -542,7 +524,7 @@
             } newSt;
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -554,7 +536,7 @@
             pold->lower_warning_limit = newSt.lower_warning_limit;
             pold->lower_alarm_limit = newSt.lower_alarm_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -571,7 +553,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
                 DBR_AL_DOUBLE;
-            status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->precision = newSt.precision.dp;
@@ -584,7 +566,7 @@
             pold->lower_warning_limit = newSt.lower_warning_limit;
             pold->lower_alarm_limit = newSt.lower_alarm_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -603,7 +585,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
                 DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -617,7 +599,7 @@
             pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
             pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -635,7 +617,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
                 DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
-            status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->precision = newSt.precision.dp;
@@ -650,7 +632,7 @@
             pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit);
             pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit);
             options = 0;
-            status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -667,7 +649,7 @@
             memset(pold, '\0', sizeof(struct dbr_ctrl_enum));
             /* first get status and severity */
             options = DBR_STATUS | DBR_ENUM_STRS;
-            status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             no_str = newSt.no_str;
@@ -677,7 +659,7 @@
                 strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i]));
             /*now get values*/
             options = 0;
-            status = dbGetField(paddr, DBR_ENUM, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -694,7 +676,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
                 DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -708,7 +690,7 @@
             pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
             pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -725,7 +707,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
                 DBR_AL_LONG;
-            status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -739,7 +721,7 @@
             pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
             pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -757,7 +739,7 @@
 
             options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
                 DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
-            status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->precision = newSt.precision.dp;
@@ -772,7 +754,7 @@
             pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
             pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
             options = 0;
-            status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
+            status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
                 nRequest, pfl);
         }
         break;
@@ -785,13 +767,13 @@
             } newSt;
 
             options = DBR_STATUS;
-            status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
+            status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
             pold->status = newSt.status;
             pold->severity = newSt.severity;
             pold->ackt = newSt.ackt;
             pold->acks = newSt.acks;
             options = 0;
-            status = dbGetField(paddr, DBR_STRING, pold->value,
+            status = dbChannelGetField(chan, DBR_STRING, pold->value,
                 &options, nRequest, pfl);
         }
         break;
@@ -807,7 +789,7 @@
                 break;
             }
             dbInitEntry(pdbbase, &dbEntry);
-            status = dbFindRecord(&dbEntry, paddr->precord->name);
+            status = dbFindRecord(&dbEntry, dbChannelRecord(chan)->name);
             if (!status) name = dbGetRecordTypeName(&dbEntry);
             dbFinishEntry(&dbEntry);
             if (status) break;
@@ -825,165 +807,163 @@
     if (status) return -1;
     return 0;
 }
-
-/* DB_PUT_FIELD put a field and convert it to the desired type */
 
-int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type,
-    const void *psrc, int no_elements)
+int dbChannel_put(struct dbChannel *chan, int src_type,
+    const void *psrc, long no_elements)
 {
     long status;
 
-    switch(src_type) {
+    switch (src_type) {
     case(oldDBR_STRING):
-        status = dbPutField(paddr, DBR_STRING, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_STRING, psrc, no_elements);
         break;
 /*  case(oldDBR_INT): */
     case(oldDBR_SHORT):
-        status = dbPutField(paddr, DBR_SHORT, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_SHORT, psrc, no_elements);
         break;
     case(oldDBR_FLOAT):
-        status = dbPutField(paddr, DBR_FLOAT, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_FLOAT, psrc, no_elements);
         break;
     case(oldDBR_ENUM):
-        status = dbPutField(paddr, DBR_ENUM, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_ENUM, psrc, no_elements);
         break;
     case(oldDBR_CHAR):
-        status = dbPutField(paddr, DBR_UCHAR, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_UCHAR, psrc, no_elements);
         break;
     case(oldDBR_LONG):
-        status = dbPutField(paddr, DBR_LONG, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_LONG, psrc, no_elements);
         break;
     case(oldDBR_DOUBLE):
-        status = dbPutField(paddr, DBR_DOUBLE, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_DOUBLE, psrc, no_elements);
         break;
 
     case(oldDBR_STS_STRING):
-        status = dbPutField(paddr, DBR_STRING,
+        status = dbChannelPutField(chan, DBR_STRING,
             ((const struct dbr_sts_string *)psrc)->value, no_elements);
         break;
 /*  case(oldDBR_STS_INT): */
     case(oldDBR_STS_SHORT):
-        status = dbPutField(paddr, DBR_SHORT,
+        status = dbChannelPutField(chan, DBR_SHORT,
             &((const struct dbr_sts_short *)psrc)->value, no_elements);
         break;
     case(oldDBR_STS_FLOAT):
-        status = dbPutField(paddr, DBR_FLOAT,
+        status = dbChannelPutField(chan, DBR_FLOAT,
             &((const struct dbr_sts_float *)psrc)->value, no_elements);
         break;
     case(oldDBR_STS_ENUM):
-        status = dbPutField(paddr, DBR_ENUM,
+        status = dbChannelPutField(chan, DBR_ENUM,
             &((const struct dbr_sts_enum *)psrc)->value, no_elements);
         break;
     case(oldDBR_STS_CHAR):
-        status = dbPutField(paddr, DBR_UCHAR,
+        status = dbChannelPutField(chan, DBR_UCHAR,
             &((const struct dbr_sts_char *)psrc)->value, no_elements);
         break;
     case(oldDBR_STS_LONG):
-        status = dbPutField(paddr, DBR_LONG,
+        status = dbChannelPutField(chan, DBR_LONG,
             &((const struct dbr_sts_long *)psrc)->value, no_elements);
         break;
     case(oldDBR_STS_DOUBLE):
-        status = dbPutField(paddr, DBR_DOUBLE,
+        status = dbChannelPutField(chan, DBR_DOUBLE,
             &((const struct dbr_sts_double *)psrc)->value, no_elements);
         break;
 
     case(oldDBR_TIME_STRING):
-        status = dbPutField(paddr, DBR_TIME,
+        status = dbChannelPutField(chan, DBR_TIME,
             ((const struct dbr_time_string *)psrc)->value, no_elements);
         break;
 /*  case(oldDBR_TIME_INT): */
     case(oldDBR_TIME_SHORT):
-        status = dbPutField(paddr, DBR_SHORT,
+        status = dbChannelPutField(chan, DBR_SHORT,
             &((const struct dbr_time_short *)psrc)->value, no_elements);
         break;
     case(oldDBR_TIME_FLOAT):
-        status = dbPutField(paddr, DBR_FLOAT,
+        status = dbChannelPutField(chan, DBR_FLOAT,
             &((const struct dbr_time_float *)psrc)->value, no_elements);
         break;
     case(oldDBR_TIME_ENUM):
-        status = dbPutField(paddr, DBR_ENUM,
+        status = dbChannelPutField(chan, DBR_ENUM,
             &((const struct dbr_time_enum *)psrc)->value, no_elements);
         break;
     case(oldDBR_TIME_CHAR):
-        status = dbPutField(paddr, DBR_UCHAR,
+        status = dbChannelPutField(chan, DBR_UCHAR,
             &((const struct dbr_time_char *)psrc)->value, no_elements);
         break;
     case(oldDBR_TIME_LONG):
-        status = dbPutField(paddr, DBR_LONG,
+        status = dbChannelPutField(chan, DBR_LONG,
             &((const struct dbr_time_long *)psrc)->value, no_elements);
         break;
     case(oldDBR_TIME_DOUBLE):
-        status = dbPutField(paddr, DBR_DOUBLE,
+        status = dbChannelPutField(chan, DBR_DOUBLE,
             &((const struct dbr_time_double *)psrc)->value, no_elements);
         break;
 
     case(oldDBR_GR_STRING):
         /* no struct dbr_gr_string - use dbr_sts_string instead */
-        status = dbPutField(paddr, DBR_STRING,
+        status = dbChannelPutField(chan, DBR_STRING,
             ((const struct dbr_sts_string *)psrc)->value, no_elements);
         break;
 /*  case(oldDBR_GR_INT): */
     case(oldDBR_GR_SHORT):
-        status = dbPutField(paddr, DBR_SHORT,
+        status = dbChannelPutField(chan, DBR_SHORT,
             &((const struct dbr_gr_short *)psrc)->value, no_elements);
         break;
     case(oldDBR_GR_FLOAT):
-        status = dbPutField(paddr, DBR_FLOAT,
+        status = dbChannelPutField(chan, DBR_FLOAT,
             &((const struct dbr_gr_float *)psrc)->value, no_elements);
         break;
     case(oldDBR_GR_ENUM):
-        status = dbPutField(paddr, DBR_ENUM,
+        status = dbChannelPutField(chan, DBR_ENUM,
             &((const struct dbr_gr_enum *)psrc)->value, no_elements);
         break;
     case(oldDBR_GR_CHAR):
-        status = dbPutField(paddr, DBR_UCHAR,
+        status = dbChannelPutField(chan, DBR_UCHAR,
             &((const struct dbr_gr_char *)psrc)->value, no_elements);
         break;
     case(oldDBR_GR_LONG):
-        status = dbPutField(paddr, DBR_LONG,
+        status = dbChannelPutField(chan, DBR_LONG,
             &((const struct dbr_gr_long *)psrc)->value, no_elements);
         break;
     case(oldDBR_GR_DOUBLE):
-        status = dbPutField(paddr, DBR_DOUBLE,
+        status = dbChannelPutField(chan, DBR_DOUBLE,
             &((const struct dbr_gr_double *)psrc)->value, no_elements);
         break;
 
     case(oldDBR_CTRL_STRING):
         /* no struct dbr_ctrl_string - use dbr_sts_string instead */
-        status = dbPutField(paddr, DBR_STRING,
+        status = dbChannelPutField(chan, DBR_STRING,
             ((const struct dbr_sts_string *)psrc)->value, no_elements);
         break;
 /*  case(oldDBR_CTRL_INT): */
     case(oldDBR_CTRL_SHORT):
-        status = dbPutField(paddr, DBR_SHORT,
+        status = dbChannelPutField(chan, DBR_SHORT,
             &((const struct dbr_ctrl_short *)psrc)->value, no_elements);
         break;
     case(oldDBR_CTRL_FLOAT):
-        status = dbPutField(paddr, DBR_FLOAT,
+        status = dbChannelPutField(chan, DBR_FLOAT,
             &((const struct dbr_ctrl_float *)psrc)->value, no_elements);
         break;
     case(oldDBR_CTRL_ENUM):
-        status = dbPutField(paddr, DBR_ENUM,
+        status = dbChannelPutField(chan, DBR_ENUM,
             &((const struct dbr_ctrl_enum *)psrc)->value, no_elements);
         break;
     case(oldDBR_CTRL_CHAR):
-        status = dbPutField(paddr, DBR_UCHAR,
+        status = dbChannelPutField(chan, DBR_UCHAR,
             &((const struct dbr_ctrl_char *)psrc)->value, no_elements);
         break;
     case(oldDBR_CTRL_LONG):
-        status = dbPutField(paddr, DBR_LONG,
+        status = dbChannelPutField(chan, DBR_LONG,
             &((const struct dbr_ctrl_long *)psrc)->value, no_elements);
         break;
     case(oldDBR_CTRL_DOUBLE):
-        status = dbPutField(paddr, DBR_DOUBLE,
+        status = dbChannelPutField(chan, DBR_DOUBLE,
             &((const struct dbr_ctrl_double *)psrc)->value, no_elements);
         break;
 
     case(oldDBR_PUT_ACKT):
-        status = dbPutField(paddr, DBR_PUT_ACKT, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_PUT_ACKT, psrc, no_elements);
         break;
     case(oldDBR_PUT_ACKS):
-        status = dbPutField(paddr, DBR_PUT_ACKS, psrc, no_elements);
+        status = dbChannelPutField(chan, DBR_PUT_ACKS, psrc, no_elements);
         break;
 
     default:
@@ -993,7 +973,7 @@
     return 0;
 }
 
-
+
 epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype)
 {
     switch(oldtype) {

=== modified file 'src/ioc/db/db_access_routines.h'
--- src/ioc/db/db_access_routines.h	2010-09-27 16:41:53 +0000
+++ src/ioc/db/db_access_routines.h	2012-05-30 18:10:27 +0000
@@ -1,11 +1,10 @@
 /*************************************************************************\
-* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE 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.
 \*************************************************************************/
 /* base/include/db_access_routines.h */
 
@@ -22,26 +21,21 @@
 #endif
 
 #include "shareLib.h"
-#include "dbAddr.h"
 
 epicsShareExtern struct dbBase *pdbbase;
 epicsShareExtern volatile int interruptAccept;
 
 
 /*
- * old db access API
- * (included here because these routines use dbAccess.h and their
- * prototypes must also be included in db_access.h)
+ * Adaptors for db_access users
  */
-epicsShareFunc int epicsShareAPI db_name_to_addr(
-    const char *pname, DBADDR *paddr);
-epicsShareFunc int epicsShareAPI db_put_field(
-    DBADDR *paddr, int src_type,const void *psrc, int no_elements);
-epicsShareFunc int epicsShareAPI db_get_field(
-    DBADDR *paddr, int dest_type,void *pdest, int no_elements, void *pfl);
-epicsShareFunc int epicsShareAPI db_get_field_and_count(
-    struct dbAddr *paddr, int buffer_type,
-    void *pbuffer, long *nRequest, void *pfl);
+epicsShareFunc struct dbChannel * dbChannel_create(const char *pname);
+epicsShareFunc int dbChannel_get(struct dbChannel *chan,
+    int buffer_type, void *pbuffer, long no_elements, void *pfl);
+epicsShareFunc int dbChannel_put(struct dbChannel *chan, int src_type,
+    const void *psrc, long no_elements);
+epicsShareFunc int epicsShareAPI dbChannel_get_count(struct dbChannel *chan,
+    int buffer_type, void *pbuffer, long *nRequest, void *pfl);
 
 
 #ifdef __cplusplus

=== modified file 'src/ioc/db/db_field_log.h'
--- src/ioc/db/db_field_log.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/db_field_log.h	2012-05-30 18:10:27 +0000
@@ -1,19 +1,26 @@
 /*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE 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.
 \*************************************************************************/
-/* $Revision-Id$
- * 	Author: 	Jeffrey O. Hill 
- *      Date:            4-1-89
+
+/*
+ *  Author: Jeffrey O. Hill <[email protected]>
+ *
+ *          Ralph Lange <[email protected]>
  */
+
 #ifndef INCLdb_field_logh
 #define INCLdb_field_logh
 
+#include "epicsTime.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -27,7 +34,7 @@
  * will most likely change infrequently.
  * 
  */
-union native_value{
+union native_value {
       	short		dbf_int;
       	short		dbf_short;
       	float		dbf_float;
@@ -36,7 +43,7 @@
       	long		dbf_long;
       	double		dbf_double;
 #ifdef DB_EVENT_LOG_STRINGS
-      	char		dbf_string[MAXSTRINGSIZE];
+        char		dbf_string[MAX_STRING_SIZE];
 #endif
 };
 
@@ -44,12 +51,48 @@
  *	structure to log the state of a data base field at the time
  *	an event is triggered.
  */
+struct db_field_log;
+typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
+
+/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
+typedef enum dbfl_type {
+    dbfl_type_rec = 0,
+    dbfl_type_val,
+    dbfl_type_ref
+} dbfl_type;
+
+/* Context of db_field_log: event = subscription update, read = read reply */
+typedef enum dbfl_context {
+    dbfl_context_read = 0,
+    dbfl_context_event
+} dbfl_context;
+
+#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
+
+struct dbfl_val {
+    union native_value field; /* Field value */
+};
+
+struct dbfl_ref {
+    dbfl_freeFunc     *dtor;  /* Callback to free filter-allocated resources */
+    void              *pvt;   /* Private pointer */
+    void              *field; /* Field value */
+};
+
 typedef struct db_field_log {
-        unsigned short		stat;	/* Alarm Status         */
-        unsigned short		sevr;	/* Alarm Severity       */
-	epicsTimeStamp		time;	/* time stamp		*/
-	union native_value	field;	/* field value		*/
-}db_field_log;
+    enum dbfl_type   type:2;  /* type (union) selector */
+    enum dbfl_context ctx:1;  /* context (operation type) */
+    epicsTimeStamp     time;  /* Time stamp */
+    unsigned short     stat;  /* Alarm Status */
+    unsigned short     sevr;  /* Alarm Severity */
+    short        field_type;  /* DBF type of data */
+    short        field_size;  /* Data size */
+    long        no_elements;  /* No of array elements */
+    union {
+        struct dbfl_val v;
+        struct dbfl_ref r;
+    } u;
+} db_field_log;
 
 #ifdef __cplusplus
 }

=== modified file 'src/ioc/db/db_test.c'
--- src/ioc/db/db_test.c	2012-05-03 17:19:34 +0000
+++ src/ioc/db/db_test.c	2012-05-30 18:10:27 +0000
@@ -5,13 +5,13 @@
 *     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.
 \*************************************************************************/
-/* share/src/db  @(#)db_test.c	1.10     2/3/94 */
+
 /*      database access subroutines */
 /*
  *      Author: Bob Dalesio
- *      Date:   4/15/88
+ *              Andrew Johnson <[email protected]>
  */
 #include <stddef.h>
 #include <epicsStdlib.h>
@@ -23,706 +23,230 @@
 #include "cadef.h"
 #include "epicsStdio.h"
 #define epicsExportSharedSymbols
+#include "dbChannel.h"
 #include "db_access_routines.h"
 #include "dbNotify.h"
 #include "db_test.h"
 #include "dbCommon.h"
 
-
 #define		MAX_ELEMS	10
-int epicsShareAPI gft(char *pname)
+
+int gft(char *pname)
 {
-	char		tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE+sizeof(struct dbr_ctrl_double)];
-	struct  dbAddr	addr;
-	struct  dbAddr	*paddr = &addr;
-	short		number_elements;
-	int	i;
-	int status;
-
-	if (pname==0 || ((*pname < ' ') || (*pname > 'z'))) {
-		printf("\nusage \"pv name\"\n");
-		return(1);
-	}
-	/* convert name to database address */
-	status=db_name_to_addr(pname,&addr);
-	if(status) {
-		printf("db_name_to_addr failed\n");
-		return(1);
-	}
-	printf("   Record Name: %s\n",pname);
-	printf("Record Address: 0x%p\n",(void *)addr.precord);
-	printf("    Field Type: %d\n",addr.dbr_field_type);
-	printf(" Field Address: 0x%p\n",addr.pfield);
-	printf("    Field Size: %d\n",addr.field_size);
-	printf("   No Elements: %ld\n",addr.no_elements);
-	number_elements =
-		((addr.no_elements > MAX_ELEMS)?MAX_ELEMS:addr.no_elements);
-
-	for(i=0; i<=LAST_BUFFER_TYPE; i++) {
-		if(addr.dbr_field_type==0) {
-			if( (i!=DBR_STRING)
-			 && (i!=DBR_STS_STRING)
-			 && (i!=DBR_TIME_STRING)
-			 && (i!=DBR_GR_STRING)
-			 && (i!=DBR_CTRL_STRING)) continue;
-		}
-		if(db_get_field(paddr,i,tgf_buffer,number_elements,NULL)<0)
-			printf("\t%s Failed\n",dbr_text[i]);
-		else
-			ca_dump_dbr (i,number_elements,tgf_buffer);
-	}
-	return(0);
+    char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE + sizeof(struct dbr_ctrl_double)];
+    struct dbChannel *chan;
+    struct dbCommon *precord;
+    long elements;
+    short type;
+    int i;
+
+    chan = dbChannel_create(pname);
+    if (!chan) {
+        printf("Channel couldn't be created\n");
+        return 1;
+    }
+
+    precord = dbChannelRecord(chan);
+    elements = dbChannelElements(chan);
+    type = dbChannelExportType(chan);
+
+    printf("   Record Name: %s\n", precord->name);
+    printf("Record Address: 0x%p\n", precord);
+    printf("   Export Type: %d\n", type);
+    printf(" Field Address: 0x%p\n", dbChannelField(chan));
+    printf("    Field Size: %d\n", dbChannelFieldSize(chan));
+    printf("   No Elements: %ld\n", elements);
+
+    if (elements > MAX_ELEMS)
+        elements = MAX_ELEMS;
+
+    for (i = 0; i <= LAST_BUFFER_TYPE; i++) {
+        if (type == 0) {
+            if ((i != DBR_STRING) && (i != DBR_STS_STRING) &&
+                (i != DBR_TIME_STRING) && (i != DBR_GR_STRING) &&
+                (i != DBR_CTRL_STRING))
+                continue;
+        }
+        if (dbChannel_get(chan, i, tgf_buffer, elements, NULL) < 0)
+            printf("\t%s Failed\n", dbr_text[i]);
+        else
+            ca_dump_dbr(i, elements, tgf_buffer);
+    }
+
+    dbChannelDelete(chan);
+    return 0;
 }
-
+
 /*
  * TPF
  * Test put field
  */
-int epicsShareAPI pft(char *pname,char *pvalue)
-{
-	struct dbAddr		addr;
-	struct dbAddr		*paddr = &addr;
-	char			buffer[500];
-	short			shortvalue;
-	long			longvalue;
-	float			floatvalue;
-	unsigned char		charvalue;
-	double			doublevalue;
-	int status;
-
-	if (pname==0 || pvalue==0
-	  || ((*pname < ' ') || (*pname > 'z'))
-	  || ((*pvalue < ' ') || (*pvalue > 'z'))){
-		printf("\nusage \"pv name\",\"value\"\n");
-		return(1);
-	}
-
-	/* convert name to database address */
-
-	/* convert name to database address */
-	status=db_name_to_addr(pname,&addr);
-	if(status) {
-		printf("db_name_to_addr failed\n");
-		return(1);
-	}
-	printf("   Record Name: %s\n",pname);
-	printf("Record Address: 0x%p\n",(void *)addr.precord);
-	printf("    Field Type: %d\n",addr.dbr_field_type);
-	printf(" Field Address: 0x%p\n",addr.pfield);
-	printf("    Field Size: %d\n",addr.field_size);
-	printf("   No Elements: %ld\n",addr.no_elements);
-	if (db_put_field(paddr,DBR_STRING,pvalue,1) < 0) printf("\n\t failed ");
-	if (db_get_field(paddr,DBR_STRING,buffer,1,NULL) < 0) printf("\n\tfailed");
-	else ca_dump_dbr(DBR_STRING,1,buffer);
-	if(addr.dbr_field_type<=DBF_STRING || addr.dbr_field_type==DBF_ENUM)
-	  return(0);
-	if(sscanf(pvalue,"%hd",&shortvalue)==1) {
-	  if (db_put_field(paddr,DBR_SHORT,&shortvalue,1) < 0) 
-		printf("\n\t SHORT failed ");
-	  if (db_get_field(paddr,DBR_SHORT,buffer,1,NULL) < 0) 
-		printf("\n\t SHORT GET failed");
-	  else ca_dump_dbr(DBR_SHORT,1,buffer);
-	}
-	if(sscanf(pvalue,"%ld",&longvalue)==1) {
-	  if (db_put_field(paddr,DBR_LONG,&longvalue,1) < 0) 
-		printf("\n\t LONG failed ");
-		if (db_get_field(paddr,DBR_LONG,buffer,1,NULL) < 0) 
-		  printf("\n\t LONG GET failed");
-		else ca_dump_dbr(DBR_LONG,1,buffer);
-	}
-	if(epicsScanFloat(pvalue, &floatvalue)==1) {
-	  if (db_put_field(paddr,DBR_FLOAT,&floatvalue,1) < 0) 
-		printf("\n\t FLOAT failed ");
-	  if (db_get_field(paddr,DBR_FLOAT,buffer,1,NULL) < 0) 
-		printf("\n\t FLOAT GET failed");
-	  else ca_dump_dbr(DBR_FLOAT,1,buffer);
-	}
-	if(epicsScanFloat(pvalue, &floatvalue)==1) {
-	  doublevalue=floatvalue;
-	  if (db_put_field(paddr,DBR_DOUBLE,&doublevalue,1) < 0)
-			printf("\n\t DOUBLE failed ");
-	  if (db_get_field(paddr,DBR_DOUBLE,buffer,1,NULL) < 0) 
-		printf("\n\t DOUBLE GET failed");
-	  else ca_dump_dbr(DBR_DOUBLE,1,buffer);
-	}
-	if(sscanf(pvalue,"%hd",&shortvalue)==1) {
-	  charvalue=(unsigned char)shortvalue;
-	  if (db_put_field(paddr,DBR_CHAR,&charvalue,1) < 0) 
-		printf("\n\t CHAR failed ");
-	  if (db_get_field(paddr,DBR_CHAR,buffer,1,NULL) < 0) 
-		printf("\n\t CHAR GET failed");
-	  else ca_dump_dbr(DBR_CHAR,1,buffer);
-	}
-	if(sscanf(pvalue,"%hd",&shortvalue)==1) {
-	  if (db_put_field(paddr,DBR_ENUM,&shortvalue,1) < 0) 
-		printf("\n\t ENUM failed ");
-	  if (db_get_field(paddr,DBR_ENUM,buffer,1,NULL) < 0) 
-		printf("\n\t ENUM GET failed");
-	  else ca_dump_dbr(DBR_ENUM,1,buffer);
-	}
-	printf("\n");
-	return(0);
-}
-
-#if 0
-/*
- * PRINT_RETURNED
- *
- * print out the values in a database access interface structure
- */
-static void print_returned(type,count,pbuffer)
-  short	type;
-  char	*pbuffer;
-  short		count;
-{
-    short i;
-
-    printf("%s\t",dbr_text[type]);
-    switch(type){
-	case (DBR_STRING):
-		for(i=0; i<count && *pbuffer!=0; i++) {
-			if(count!=1 && (i%5 == 0)) printf("\n");
-			printf("%s ",pbuffer);
-			pbuffer += MAX_STRING_SIZE;
-		}
-		break;
-	case (DBR_SHORT):
-	case (DBR_ENUM):
-	{
-		short *pvalue = (short *)pbuffer;
-		for (i = 0; i < count; i++,pvalue++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*(short *)pvalue);
-		}
-		break;
-	}
-	case (DBR_FLOAT):
-	{
-		float *pvalue = (float *)pbuffer;
-		for (i = 0; i < count; i++,pvalue++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",*(float *)pvalue);
-		}
-		break;
-	}
-	case (DBR_CHAR):
-	{
-		int	value;
-
-		for (i = 0; i < count; i++,pbuffer++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			value = *(unsigned char *)pbuffer;
-			printf("%d ",value);
-		}
-		break;
-	}
-	case (DBR_LONG):
-	{
-		long *pvalue = (long *)pbuffer;
-		for (i = 0; i < count; i++,pvalue++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%ld ", (unsigned long)*pvalue);
-		}
-		break;
-	}
-	case (DBR_DOUBLE):
-	{
-		double *pvalue = (double *)pbuffer;
-		for (i = 0; i < count; i++,pvalue++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",(float)(*pvalue));
-		}
-		break;
-	}
-	case (DBR_STS_STRING):
-	case (DBR_GR_STRING):
-	case (DBR_CTRL_STRING):
-	{
-		struct dbr_sts_string *pvalue 
-		  = (struct dbr_sts_string *) pbuffer;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tValue: %s",pvalue->value);
-		break;
-	}
-	case (DBR_STS_ENUM):
-	case (DBR_STS_SHORT):
-	{
-		struct dbr_sts_short *pvalue
-		  = (struct dbr_sts_short *)pbuffer;
-		short *pshort = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pshort++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*pshort);
-		}
-		break;
-	}
-	case (DBR_STS_FLOAT):
-	{
-		struct dbr_sts_float *pvalue
-		  = (struct dbr_sts_float *)pbuffer;
-		float *pfloat = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pfloat++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",*pfloat);
-		}
-		break;
-	}
-	case (DBR_STS_CHAR):
-	{
-		struct dbr_sts_char *pvalue
-		  = (struct dbr_sts_char *)pbuffer;
-		unsigned char *pchar = &pvalue->value;
-		short value;
-
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pchar++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			value=*pchar;
-			printf("%d ",value);
-		}
-		break;
-	}
-	case (DBR_STS_LONG):
-	{
-		struct dbr_sts_long *pvalue
-		  = (struct dbr_sts_long *)pbuffer;
-		dbr_long_t *plong = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,plong++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*plong);
-		}
-		break;
-	}
-	case (DBR_STS_DOUBLE):
-	{
-		struct dbr_sts_double *pvalue
-		  = (struct dbr_sts_double *)pbuffer;
-		double *pdouble = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pdouble++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",(float)(*pdouble));
-		}
-		break;
-	}
-	case (DBR_TIME_STRING):
-	{
-		struct dbr_time_string *pvalue 
-		  = (struct dbr_time_string *) pbuffer;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		printf("\tValue: ");
-		printf("%s",pvalue->value);
-		break;
-	}
-	case (DBR_TIME_SHORT):
-	case (DBR_TIME_ENUM):
-	{
-		struct dbr_time_short *pvalue
-		  = (struct dbr_time_short *)pbuffer;
-		short *pshort = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pshort++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*pshort);
-		}
-		break;
-	}
-	case (DBR_TIME_FLOAT):
-	{
-		struct dbr_time_float *pvalue
-		  = (struct dbr_time_float *)pbuffer;
-		float *pfloat = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pfloat++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",*pfloat);
-		}
-		break;
-	}
-	case (DBR_TIME_CHAR):
-	{
-		struct dbr_time_char *pvalue
-		  = (struct dbr_time_char *)pbuffer;
-		unsigned char *pchar = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pchar++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",(short)(*pchar));
-		}
-		break;
-	}
-	case (DBR_TIME_LONG):
-	{
-		struct dbr_time_long *pvalue
-		  = (struct dbr_time_long *)pbuffer;
-		dbr_long_t *plong = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,plong++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*plong);
-		}
-		break;
-	}
-	case (DBR_TIME_DOUBLE):
-	{
-		struct dbr_time_double *pvalue
-		  = (struct dbr_time_double *)pbuffer;
-		double *pdouble = &pvalue->value;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf("\tTimeStamp: %u %u",
-			pvalue->stamp.secPastEpoch, pvalue->stamp.nsec);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pdouble++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",(float)(*pdouble));
-		}
-		break;
-	}
-	case (DBR_GR_SHORT):
-	{
-		struct dbr_gr_short *pvalue
-		  = (struct dbr_gr_short *)pbuffer;
-		short *pshort = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pshort++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*pshort);
-		}
-		break;
-	}
-	case (DBR_GR_FLOAT):
-	{
-		struct dbr_gr_float *pvalue
-		  = (struct dbr_gr_float *)pbuffer;
-		float *pfloat = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f",
-		  pvalue->precision,
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pfloat++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",*pfloat);
-		}
-		break;
-	}
-	case (DBR_GR_ENUM):
-	case (DBR_CTRL_ENUM):
-	{
-		struct dbr_gr_enum *pvalue
-		  = (struct dbr_gr_enum *)pbuffer;
-		printf("%2d %2d",pvalue->status,
-			pvalue->severity);
-		printf("\tValue: %d",pvalue->value);
-		if(pvalue->no_str>0) {
-			printf("\n\t%3d",pvalue->no_str);
-			for (i = 0; i < pvalue->no_str; i++)
-				printf("\n\t%.26s",pvalue->strs[i]);
-		}
-		break;
-	}
-	case (DBR_GR_CHAR):
-	{
-		struct dbr_gr_char *pvalue
-		  = (struct dbr_gr_char *)pbuffer;
-		unsigned char *pchar = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pchar++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",(short)(*pchar));
-		}
-		break;
-	}
-	case (DBR_GR_LONG):
-	{
-		struct dbr_gr_long *pvalue
-		  = (struct dbr_gr_long *)pbuffer;
-		dbr_long_t *plong = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,plong++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*plong);
-		}
-		break;
-	}
-	case (DBR_GR_DOUBLE):
-	{
-		struct dbr_gr_double *pvalue
-		  = (struct dbr_gr_double *)pbuffer;
-		double *pdouble = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f",
-		  pvalue->precision,
-		  (float)(pvalue->upper_disp_limit),
-		  (float)(pvalue->lower_disp_limit),
-		  (float)(pvalue->upper_alarm_limit),
-		  (float)(pvalue->upper_warning_limit),
-		  (float)(pvalue->lower_warning_limit),
-		  (float)(pvalue->lower_alarm_limit));
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pdouble++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",(float)(*pdouble));
-		}
-		break;
-	}
-	case (DBR_CTRL_SHORT):
-	{
-		struct dbr_ctrl_short *pvalue
-		  = (struct dbr_ctrl_short *)pbuffer;
-		short *pshort = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		printf(" %8d %8d",
-		  pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pshort++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*pshort);
-		}
-		break;
-	}
-	case (DBR_CTRL_FLOAT):
-	{
-		struct dbr_ctrl_float *pvalue
-		  = (struct dbr_ctrl_float *)pbuffer;
-		float *pfloat = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f",
-		  pvalue->precision,
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		printf(" %8.3f %8.3f",
-		  pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pfloat++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.4f ",*pfloat);
-		}
-		break;
-	}
-	case (DBR_CTRL_CHAR):
-	{
-		struct dbr_ctrl_char *pvalue
-		  = (struct dbr_ctrl_char *)pbuffer;
-		unsigned char *pchar = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		printf(" %8d %8d",
-		  pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pchar++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%4d ",(short)(*pchar));
-		}
-		break;
-	}
-	case (DBR_CTRL_LONG):
-	{
-		struct dbr_ctrl_long *pvalue
-		  = (struct dbr_ctrl_long *)pbuffer;
-		dbr_long_t *plong = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf("\n\t%8d %8d %8d %8d %8d %8d",
-		  pvalue->upper_disp_limit,pvalue->lower_disp_limit,
-		  pvalue->upper_alarm_limit,pvalue->upper_warning_limit,
-		  pvalue->lower_warning_limit,pvalue->lower_alarm_limit);
-		printf(" %8d %8d",
-		  pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit);
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,plong++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%d ",*plong);
-		}
-		break;
-	}
-	case (DBR_CTRL_DOUBLE):
-	{
-		struct dbr_ctrl_double *pvalue
-		  = (struct dbr_ctrl_double *)pbuffer;
-		double *pdouble = &pvalue->value;
-		printf("%2d %2d %.8s",pvalue->status,pvalue->severity,
-			pvalue->units);
-		printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f",
-		  pvalue->precision,
-		  (float)(pvalue->upper_disp_limit),
-		  (float)(pvalue->lower_disp_limit),
-		  (float)(pvalue->upper_alarm_limit),
-		  (float)(pvalue->upper_warning_limit),
-		  (float)(pvalue->lower_warning_limit),
-		  (float)(pvalue->lower_alarm_limit));
-		printf(" %8.3f %8.3f",
-		  (float)(pvalue->upper_ctrl_limit),
-		  (float)(pvalue->lower_ctrl_limit));
-		if(count==1) printf("\tValue: ");
-		for (i = 0; i < count; i++,pdouble++){
-			if(count!=1 && (i%10 == 0)) printf("\n");
-			printf("%6.6f ",(float)(*pdouble));
-		}
-		break;
-	}
-	case (DBR_STSACK_STRING):
-	{
-		struct dbr_stsack_string *pvalue
-		  = (struct dbr_stsack_string *)pbuffer;
-		printf("%2d %2d",pvalue->status,pvalue->severity);
-		printf(" %2d %2d",pvalue->ackt,pvalue->acks);
-		printf(" %s",pvalue->value);
-		break;
-	}
-	case (DBR_CLASS_NAME):
-	{
-		printf(" %s",(char *)pbuffer);
-		break;
-	}
+int epicsShareAPI pft(char *pname, char *pvalue)
+{
+    struct dbChannel *chan;
+    struct dbCommon *precord;
+    long elements;
+    short type;
+    char buffer[500];
+    short shortvalue;
+    long longvalue;
+    float floatvalue;
+    unsigned char charvalue;
+    double doublevalue;
+
+    chan = dbChannel_create(pname);
+    if (!chan) {
+        printf("Channel couldn't be created\n");
+        return 1;
+    }
+
+    precord = dbChannelRecord(chan);
+    elements = dbChannelElements(chan);
+    type = dbChannelExportType(chan);
+
+    printf("   Record Name: %s\n", precord->name);
+    printf("Record Address: 0x%p\n", precord);
+    printf("   Export Type: %d\n", type);
+    printf(" Field Address: 0x%p\n", dbChannelField(chan));
+    printf("    Field Size: %d\n", dbChannelFieldSize(chan));
+    printf("   No Elements: %ld\n", elements);
+
+    if (dbChannel_put(chan, DBR_STRING,pvalue, 1) < 0)
+        printf("\n\t failed ");
+    if (dbChannel_get(chan, DBR_STRING,buffer, 1, NULL) < 0)
+        printf("\n\tfailed");
+    else
+        ca_dump_dbr(DBR_STRING,1, buffer);
+
+    if (type <= DBF_STRING || type == DBF_ENUM)
+        return 0;
+
+    if (sscanf(pvalue, "%hd", &shortvalue) == 1) {
+        if (dbChannel_put(chan, DBR_SHORT,&shortvalue, 1) < 0)
+            printf("\n\t SHORT failed ");
+        if (dbChannel_get(chan, DBR_SHORT,buffer, 1, NULL) < 0)
+            printf("\n\t SHORT GET failed");
+        else
+            ca_dump_dbr(DBR_SHORT,1, buffer);
+    }
+    if (sscanf(pvalue, "%ld", &longvalue) == 1) {
+        if (dbChannel_put(chan, DBR_LONG,&longvalue, 1) < 0)
+            printf("\n\t LONG failed ");
+        if (dbChannel_get(chan, DBR_LONG,buffer, 1, NULL) < 0)
+            printf("\n\t LONG GET failed");
+        else
+            ca_dump_dbr(DBR_LONG,1, buffer);
+    }
+    if (epicsScanFloat(pvalue, &floatvalue) == 1) {
+        if (dbChannel_put(chan, DBR_FLOAT,&floatvalue, 1) < 0)
+            printf("\n\t FLOAT failed ");
+        if (dbChannel_get(chan, DBR_FLOAT,buffer, 1, NULL) < 0)
+            printf("\n\t FLOAT GET failed");
+        else
+            ca_dump_dbr(DBR_FLOAT,1, buffer);
+    }
+    if (epicsScanFloat(pvalue, &floatvalue) == 1) {
+        doublevalue = floatvalue;
+        if (dbChannel_put(chan, DBR_DOUBLE,&doublevalue, 1) < 0)
+            printf("\n\t DOUBLE failed ");
+        if (dbChannel_get(chan, DBR_DOUBLE,buffer, 1, NULL) < 0)
+            printf("\n\t DOUBLE GET failed");
+        else
+            ca_dump_dbr(DBR_DOUBLE,1, buffer);
+    }
+    if (sscanf(pvalue, "%hd", &shortvalue) == 1) {
+        charvalue = (unsigned char) shortvalue;
+        if (dbChannel_put(chan, DBR_CHAR,&charvalue, 1) < 0)
+            printf("\n\t CHAR failed ");
+        if (dbChannel_get(chan, DBR_CHAR,buffer, 1, NULL) < 0)
+            printf("\n\t CHAR GET failed");
+        else
+            ca_dump_dbr(DBR_CHAR,1, buffer);
+    }
+    if (sscanf(pvalue, "%hd", &shortvalue) == 1) {
+        if (dbChannel_put(chan, DBR_ENUM,&shortvalue, 1) < 0)
+            printf("\n\t ENUM failed ");
+        if (dbChannel_get(chan, DBR_ENUM,buffer, 1, NULL) < 0)
+            printf("\n\t ENUM GET failed");
+        else
+            ca_dump_dbr(DBR_ENUM,1, buffer);
     }
     printf("\n");
+    return (0);
 }
-#endif
-
+
+
 typedef struct tpnInfo {
     epicsEventId callbackDone;
-    putNotify    *ppn;
-}tpnInfo;
+    putNotify *ppn;
+    struct dbChannel *chan;
+} tpnInfo;
 
 static void tpnCallback(putNotify *ppn)
 {
-    struct dbAddr   *pdbaddr = (struct dbAddr *)ppn->paddr;
-    tpnInfo         *ptpnInfo = (tpnInfo *)ppn->usrPvt;
+    tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt;
     putNotifyStatus status = ppn->status;
-    char	    *pname = pdbaddr->precord->name;
+    const char *pname = dbChannelRecord(ppn->chan)->name;
 
-    if(status==0)
-	printf("tpnCallback: success record=%s\n",pname);
+    if (status == 0)
+        printf("tpnCallback: success record=%s\n", pname);
     else
-        printf("%s tpnCallback status = %d\n",pname,status);
+        printf("%s tpnCallback status = %d\n", pname, status);
     epicsEventSignal(ptpnInfo->callbackDone);
 }
 
 static void tpnThread(void *pvt)
 {
-    tpnInfo   *ptpnInfo = (tpnInfo *)pvt;
-    putNotify *ppn = (putNotify *)ptpnInfo->ppn;
+    tpnInfo *ptpnInfo = (tpnInfo *) pvt;
+    putNotify *ppn = (putNotify *) ptpnInfo->ppn;
 
     dbPutNotify(ppn);
     epicsEventWait(ptpnInfo->callbackDone);
     dbNotifyCancel(ppn);
     epicsEventDestroy(ptpnInfo->callbackDone);
-    free((void *)ppn->paddr);
+    free(ppn->pbuffer);
+    dbChannelDelete(ppn->chan);
     free(ppn);
     free(ptpnInfo);
 }
 
-
-int epicsShareAPI tpn(char	*pname,char *pvalue)
+int epicsShareAPI tpn(char *pname, char *pvalue)
 {
-    long	   status;
-    tpnInfo       *ptpnInfo;
-    struct dbAddr *pdbaddr=NULL;
-    putNotify	   *ppn=NULL;
-    char	   *psavevalue;
-    int		   len;
-
-    if (pname==0 || pvalue==0
-      || ((*pname < ' ') || (*pname > 'z'))
-      || ((*pvalue < ' ') || (*pvalue > 'z'))){
-    	printf("\nusage \"pv name\",\"value\"\n");
-    	return(1);
-    }
-    len = strlen(pvalue);
-    /*allocate space for value immediately following DBADDR*/
-    pdbaddr = calloc(1,sizeof(struct dbAddr) + len+1);
-    if(!pdbaddr) {
-	printf("calloc failed\n");
-	return(-1);
-    }
-    psavevalue = (char *)(pdbaddr + 1);
-    strcpy(psavevalue,pvalue);
-    status = db_name_to_addr(pname,pdbaddr);
-    if(status) {
-	printf("db_name_to_addr failed\n");
-	free((void *)pdbaddr);
-	return(-1);
-    }
-    ppn = calloc(1,sizeof(putNotify));
-    if(!pdbaddr) {
-	printf("calloc failed\n");
-	return(-1);
-    }
-    ppn->paddr = pdbaddr;
-    ppn->pbuffer = psavevalue;
+    struct dbChannel *chan;
+    tpnInfo *ptpnInfo;
+    putNotify *ppn;
+    char *pbuffer;
+
+    chan = dbChannel_create(pname);
+    if (!chan) {
+        printf("Channel couldn't be created\n");
+        return 1;
+    }
+
+    pbuffer = strdup(pvalue);
+    ppn = calloc(1, sizeof(putNotify));
+    if (!ppn) {
+        printf("calloc failed\n");
+        return -1;
+    }
+    ppn->chan = chan;
+    ppn->pbuffer = pbuffer;
     ppn->nRequest = 1;
-    if(dbPutNotifyMapType(ppn,DBR_STRING)) {
-	printf("dbPutNotifyMapType failed\n");
-	printf("calloc failed\n");
-	return(-1);
+    if (dbPutNotifyMapType(ppn, DBR_STRING)) {
+        printf("dbPutNotifyMapType failed\n");
+        printf("calloc failed\n");
+        return -1;
     }
     ppn->userCallback = tpnCallback;
-    ptpnInfo = calloc(1,sizeof(tpnInfo));
-    if(!ptpnInfo) {
-	printf("calloc failed\n");
-	return(-1);
+    ptpnInfo = calloc(1, sizeof(tpnInfo));
+    if (!ptpnInfo) {
+        printf("calloc failed\n");
+        return -1;
     }
     ptpnInfo->ppn = ppn;
     ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty);
     ppn->usrPvt = ptpnInfo;
-    epicsThreadCreate("tpn",epicsThreadPriorityHigh,
-        epicsThreadGetStackSize(epicsThreadStackMedium),
-        tpnThread,ptpnInfo);
-    return(0);
+    epicsThreadCreate("tpn", epicsThreadPriorityHigh,
+    epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo);
+    return 0;
 }

=== added directory 'src/ioc/db/filters'
=== added file 'src/ioc/db/filters/arr.c'
--- src/ioc/db/filters/arr.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/arr.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,205 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <stdio.h>
+
+#include <epicsExport.h>
+#include <freeList.h>
+#include <dbAccess.h>
+#include <dbExtractArray.h>
+#include <db_field_log.h>
+#include <recSup.h>
+#include <special.h>
+#include <chfPlugin.h>
+
+typedef struct myStruct {
+    epicsInt32 start;
+    epicsInt32 incr;
+    epicsInt32 end;
+    void *arrayFreeList;
+    long no_elements;
+} myStruct;
+
+static void *myStructFreeList;
+
+static const
+chfPluginArgDef opts[] = {
+    chfInt32 (myStruct, start, "s", 0, 1),
+    chfInt32 (myStruct, incr, "i", 0, 1),
+    chfInt32 (myStruct, end, "e", 0, 1),
+    chfPluginArgEnd
+};
+
+static void * allocPvt(void)
+{
+    myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
+    my->incr = 1;
+    my->end = -1;
+    return (void *) my;
+}
+
+static void freePvt(void *pvt)
+{
+    myStruct *my = (myStruct*) pvt;
+
+    if (my->arrayFreeList) freeListCleanup(my->arrayFreeList);
+    freeListFree(myStructFreeList, pvt);
+}
+
+static int parse_ok(void *pvt)
+{
+    myStruct *my = (myStruct*) pvt;
+
+    if (my->incr <= 0) my->incr = 1;
+    return 0;
+}
+
+static void freeArray(db_field_log *pfl) {
+    if (pfl->type == dbfl_type_ref) {
+        freeListFree(pfl->u.r.pvt, pfl->u.r.field);
+    }
+}
+
+static long wrapArrayIndices(long *start, const long increment, long *end, const long no_elements) {
+    long len = 0;
+
+    if (*start < 0) *start = no_elements + *start;
+    if (*start < 0) *start = 0;
+    if (*start > no_elements) *start = no_elements;
+
+    if (*end < 0) *end = no_elements + *end;
+    if (*end < 0) *end = 0;
+    if (*end >= no_elements) *end = no_elements - 1;
+
+    if (*end - *start >= 0) len = 1 + (*end - *start) / increment;
+    return len;
+}
+
+static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
+    myStruct *my = (myStruct*) pvt;
+    struct dbCommon *prec;
+    struct rset *prset;
+    long start = my->start;
+    long end = my->end;
+    long nTarget = 0;
+    long offset = 0;
+    long status;
+    long nSource = chan->addr.no_elements;
+
+    /* Only array data */
+    if (pfl->type == dbfl_type_val) {
+        return pfl;
+
+    /* Extract from record */
+    } else if (pfl->type == dbfl_type_rec) {
+        if (chan->addr.special == SPC_DBADDR &&
+            nSource > 1 &&
+            (prset = dbGetRset(&chan->addr)) &&
+            prset->get_array_info) {
+            status = prset->get_array_info(&chan->addr, &nSource, &offset);
+            nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
+            prec = dbChannelRecord(chan);
+            pfl->type = dbfl_type_ref;
+            pfl->stat = prec->stat;
+            pfl->sevr = prec->sevr;
+            pfl->time = prec->time;
+            pfl->field_type = chan->addr.field_type;
+            pfl->field_size = chan->addr.field_size;
+            pfl->no_elements = nTarget;
+            if (nTarget) {
+                pfl->u.r.dtor = freeArray;
+                pfl->u.r.pvt = my->arrayFreeList;
+                void *pdst = freeListCalloc(my->arrayFreeList);
+                offset = (offset + start) % chan->addr.no_elements;
+                dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr);
+                pfl->u.r.field = pdst;
+            }
+        }
+
+    /* Extract from buffer */
+    } else if (pfl->type == dbfl_type_ref) {
+        nSource = pfl->no_elements;
+        nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
+        pfl->no_elements = nTarget;
+        void *psrc = pfl->u.r.field;
+        void *pdst = NULL;
+        if (nTarget) {                                        /* Copy the data out */
+            pdst = freeListCalloc(my->arrayFreeList);
+            offset = start;
+            dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr);
+        }
+        if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
+        if (nTarget) {
+            pfl->u.r.dtor = freeArray;
+            pfl->u.r.pvt = my->arrayFreeList;
+            pfl->u.r.field = pdst;
+        }
+    }
+    return pfl;
+}
+
+static void channelRegisterPost(dbChannel *chan, void *pvt,
+                                chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    myStruct *my = (myStruct*) pvt;
+    long start = my->start;
+    long end = my->end;
+    long max = 0;
+
+    if (probe->no_elements <= 1) return;                                    /* array data only */
+
+    max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements);     /* wrap indices into array */
+    if (max) {
+        if (!my->arrayFreeList)
+            freeListInitPvt(&my->arrayFreeList, max * probe->field_size, 2);
+        if (!my->arrayFreeList) return;
+    }
+    probe->no_elements = my->no_elements = max;
+    *cb_out = filter;
+    *arg_out = pvt;
+}
+
+static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
+{
+    myStruct *my = (myStruct*) pvt;
+    printf("%*s  plugin arr, start=%d, incr=%d, end=%d\n", indent, "",
+           my->start, my->incr, my->end);
+}
+
+static chfPluginIf pif = {
+    allocPvt,
+    freePvt,
+
+    NULL, /* parse_error, */
+    parse_ok,
+
+    NULL, /* channel_open, */
+    NULL, /* channelRegisterPre, */
+    channelRegisterPost,
+    channel_report,
+    NULL /* channel_close */
+};
+
+static void arrInitialize(void)
+{
+    static int firstTime = 1;
+
+    if (!firstTime) return;
+    firstTime = 0;
+
+    if (!myStructFreeList)
+        freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
+
+    chfPluginRegister("arr", &pif, opts);
+}
+
+epicsExportRegistrar(arrInitialize);

=== added file 'src/ioc/db/filters/dbnd.c'
--- src/ioc/db/filters/dbnd.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/dbnd.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,142 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <stdio.h>
+
+#include <epicsExport.h>
+#include <epicsMath.h>
+#include <freeList.h>
+#include <dbConvertFast.h>
+#include <chfPlugin.h>
+#include <db_field_log.h>
+
+typedef struct myStruct {
+    int    mode;
+    double cval;
+    double hyst;
+    double last;
+} myStruct;
+
+static void *myStructFreeList;
+
+static const
+chfPluginEnumType modeEnum[] = { {"abs", 0}, {"rel", 1}, {NULL,0} };
+
+static const
+chfPluginArgDef opts[] = {
+    chfDouble (myStruct, cval, "d", 0, 1),
+    chfEnum   (myStruct, mode, "m", 0, 1, modeEnum),
+    chfPluginArgEnd
+};
+
+static void * allocPvt(void)
+{
+    return freeListCalloc(myStructFreeList);
+}
+
+static void freePvt(void *pvt)
+{
+    freeListFree(myStructFreeList, pvt);
+}
+
+static int parse_ok(void *pvt)
+{
+    myStruct *my = (myStruct*) pvt;
+    my->hyst = my->cval;
+    my->last = epicsNAN;
+    return 0;
+}
+
+static void shiftval (myStruct *my, double val) {
+    my->last = val;
+    if (my->mode == 1)
+        my->hyst = val * my->cval/100.;
+}
+
+static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
+    myStruct *my = (myStruct*) pvt;
+    long status;
+    double val, delta;
+    short drop = 0;
+
+    /*
+     * Only scalar values supported - strings, arrays, and conversion errors
+     * are just passed on
+     */
+    if (pfl->type == dbfl_type_val) {
+        DBADDR localAddr = chan->addr; /* Structure copy */
+        localAddr.field_type = pfl->field_type;
+        localAddr.field_size = pfl->field_size;
+        localAddr.no_elements = pfl->no_elements;
+        localAddr.pfield = (char *) &pfl->u.v.field;
+        status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE]
+                 (localAddr.pfield, (void*) &val, &localAddr);
+        if (!status) {
+            if (isnan(my->last)) {
+                shiftval(my, val);
+            } else {
+                delta = fabs(my->last - val);
+                if (delta <= my->hyst) {
+                    drop = 1;
+                } else {
+                    shiftval(my, val);
+                }
+            }
+        }
+    }
+    if (drop) return NULL;
+    else return pfl;
+}
+
+static void channelRegisterPre(dbChannel *chan, void *pvt,
+                               chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    *cb_out = filter;
+    *arg_out = pvt;
+}
+
+static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
+{
+    myStruct *my = (myStruct*) pvt;
+    printf("%*s  plugin dbnd, mode=%s, delta=%g%s\n", indent, "",
+           chfPluginEnumString(modeEnum, my->mode, "n/a"), my->cval,
+           my->mode == 1 ? "%" : "");
+}
+
+static chfPluginIf pif = {
+    allocPvt,
+    freePvt,
+
+    NULL, /* parse_error, */
+    parse_ok,
+
+    NULL, /* channel_open, */
+    channelRegisterPre,
+    NULL, /* channelRegisterPost, */
+    channel_report,
+    NULL /* channel_close */
+};
+
+static void dbndInitialize(void)
+{
+    static int firstTime = 1;
+
+    if (!firstTime) return;
+    firstTime = 0;
+
+    if (!myStructFreeList)
+        freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
+
+    chfPluginRegister("dbnd", &pif, opts);
+}
+
+epicsExportRegistrar(dbndInitialize);

=== added file 'src/ioc/db/filters/filters.dbd'
--- src/ioc/db/filters/filters.dbd	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/filters.dbd	2012-05-30 18:10:27 +0000
@@ -0,0 +1,4 @@
+registrar(tsInitialize)
+registrar(dbndInitialize)
+registrar(arrInitialize)
+registrar(syncInitialize)

=== added file 'src/ioc/db/filters/sync.c'
--- src/ioc/db/filters/sync.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/sync.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,170 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <stdio.h>
+
+#include "epicsExport.h"
+#include "freeList.h"
+#include "db_field_log.h"
+#include "chfPlugin.h"
+#include "dbState.h"
+
+#define STATE_NAME_LENGTH 20
+
+static const
+chfPluginEnumType modeEnum[] = { {"before", 0}, {"first", 1},
+                                 {"last", 2}, {"after", 3},
+                                 {"while", 4}, {"unless", 5},
+                                 {NULL,0} };
+typedef enum syncMode {
+    syncModeBefore=0,
+    syncModeFirst=1,
+    syncModeLast=2,
+    syncModeAfter=3,
+    syncModeWhile=4,
+    syncModeUnless=5
+} syncMode;
+
+typedef struct myStruct {
+    syncMode mode;
+    char state[STATE_NAME_LENGTH];
+    dbStateId id;
+    db_field_log *lastfl;
+    int laststate:1;
+} myStruct;
+
+static void *myStructFreeList;
+
+static const
+chfPluginArgDef opts[] = {
+    chfEnum   (myStruct, mode,  "m", 1, 1, modeEnum),
+    chfString (myStruct, state, "s", 1, 0),
+    chfPluginArgEnd
+};
+
+static void * allocPvt(void)
+{
+    myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
+    return (void *) my;
+}
+
+static void freePvt(void *pvt)
+{
+    freeListFree(myStructFreeList, pvt);
+}
+
+static int parse_ok(void *pvt)
+{
+    myStruct *my = (myStruct*) pvt;
+
+    if (!(my->id = dbStateFind(my->state)))
+        return -1;
+
+    return 0;
+}
+
+static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
+    if (pfl->ctx == dbfl_context_read)
+        return pfl;
+
+    db_field_log *passfl = NULL;
+    myStruct *my = (myStruct*) pvt;
+    int actstate = dbStateGet(my->id);
+
+    switch (my->mode) {
+    case syncModeBefore:
+        if (actstate && !my->laststate) {
+            passfl = my->lastfl;
+            my->lastfl = NULL;
+        }
+        break;
+    case syncModeFirst:
+        if (actstate && !my->laststate) {
+            passfl = pfl;
+            pfl = NULL;
+        }
+        break;
+    case syncModeLast:
+        if (!actstate && my->laststate) {
+            passfl = my->lastfl;
+            my->lastfl = NULL;
+        }
+        break;
+    case syncModeAfter:
+        if (!actstate && my->laststate) {
+            passfl = pfl;
+            pfl = NULL;
+        }
+        break;
+    case syncModeWhile:
+        if (actstate) {
+            passfl = pfl;
+        }
+        goto no_shift;
+    case syncModeUnless:
+        if (!actstate) {
+            passfl = pfl;
+        }
+        goto no_shift;
+    }
+
+    if (my->lastfl)
+        db_delete_field_log(my->lastfl);
+    my->lastfl = pfl;
+    my->laststate = actstate;
+
+    no_shift:
+    return passfl;
+}
+
+static void channelRegisterPre(dbChannel *chan, void *pvt,
+                               chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    *cb_out = filter;
+    *arg_out = pvt;
+}
+
+static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
+{
+    myStruct *my = (myStruct*) pvt;
+    printf("%*s  plugin sync, mode=%s, state=%s\n", indent, "",
+           chfPluginEnumString(modeEnum, my->mode, "n/a"), my->state);
+}
+
+static chfPluginIf pif = {
+    allocPvt,
+    freePvt,
+
+    NULL, /* parse_error, */
+    parse_ok,
+
+    NULL, /* channel_open, */
+    channelRegisterPre,
+    NULL, /* channelRegisterPost, */
+    channel_report,
+    NULL /* channel_close */
+};
+
+static void syncInitialize(void)
+{
+    static int firstTime = 1;
+
+    if (!firstTime) return;
+    firstTime = 0;
+
+    if (!myStructFreeList)
+        freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
+
+    chfPluginRegister("sync", &pif, opts);
+}
+
+epicsExportRegistrar(syncInitialize);

=== added directory 'src/ioc/db/filters/test'
=== added file 'src/ioc/db/filters/test/Makefile'
--- src/ioc/db/filters/test/Makefile	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/Makefile	2012-05-30 18:10:27 +0000
@@ -0,0 +1,47 @@
+#*************************************************************************
+# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
+#     National Laboratory.
+# Copyright (c) 2002 The Regents of the University of California, as
+#     Operator of Los Alamos National Laboratory.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution. 
+#*************************************************************************
+TOP=../../../../..
+
+include $(TOP)/configure/CONFIG
+
+PROD_LIBS += dbCore ca Com
+
+TESTPROD_HOST += tsTest
+tsTest_SRCS += tsTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += tsTest
+TESTS += tsTest
+
+TESTPROD_HOST += dbndTest
+dbndTest_SRCS += dbndTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += dbndTest
+TESTS += dbndTest
+
+TARGETS += $(COMMON_DIR)/arrTest.dbd
+arrTest_DBD += arrRecord.dbd
+TESTPROD_HOST += arrTest
+arrTest_SRCS += arrTest.cpp arrRecord.c arrRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += arrTest
+TESTS += arrTest
+
+TESTPROD_HOST += syncTest
+syncTest_SRCS += syncTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += syncTest
+TESTS += syncTest
+
+TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+
+include $(TOP)/configure/RULES
+
+$(COMMON_DIR)/xRecord.dbd: ../../../test/xRecord.dbd
+	$(INSTALL) -d $< $(@D)
+
+tsTest$(OBJ): $(COMMON_DIR)/xRecord.h
+dbndTest$(OBJ): $(COMMON_DIR)/xRecord.h
+syncTest$(OBJ): $(COMMON_DIR)/xRecord.h
+arrTest$(OBJ): $(COMMON_DIR)/arrRecord.h

=== added file 'src/ioc/db/filters/test/arrRecord.c'
--- src/ioc/db/filters/test/arrRecord.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/arrRecord.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,135 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/* arrRecord.c - minimal array record for test purposes: no processing */
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ *
+ * vaguely implemented like parts of recWaveform.c by Bob Dalesio
+ *
+ */
+
+#include <stdio.h>
+
+#include "dbDefs.h"
+#include "epicsPrint.h"
+#include "dbAccess.h"
+#include "dbEvent.h"
+#include "dbFldTypes.h"
+#include "recSup.h"
+#include "recGbl.h"
+#include "cantProceed.h"
+#define GEN_SIZE_OFFSET
+#include "arrRecord.h"
+#undef  GEN_SIZE_OFFSET
+#include "epicsExport.h"
+
+/* Create RSET - Record Support Entry Table*/
+#define report NULL
+#define initialize NULL
+static long init_record(arrRecord *, int);
+static long process(arrRecord *);
+#define special NULL
+#define get_value NULL
+static long cvt_dbaddr(DBADDR *);
+static long get_array_info(DBADDR *, long *, long *);
+static long put_array_info(DBADDR *, long);
+#define get_units NULL
+#define get_precision NULL
+#define get_enum_str NULL
+#define get_enum_strs NULL
+#define put_enum_str NULL
+#define get_graphic_double NULL
+#define get_control_double NULL
+#define get_alarm_double NULL
+
+rset arrRSET = {
+        RSETNUMBER,
+        report,
+        initialize,
+        init_record,
+        process,
+        special,
+        get_value,
+        cvt_dbaddr,
+        get_array_info,
+        put_array_info,
+        get_units,
+        get_precision,
+        get_enum_str,
+        get_enum_strs,
+        put_enum_str,
+        get_graphic_double,
+        get_control_double,
+        get_alarm_double
+};
+epicsExportAddress(rset, arrRSET);
+
+static long init_record(arrRecord *prec, int pass)
+{
+    if (pass == 0) {
+        if (prec->nelm <= 0)
+            prec->nelm = 1;
+        if (prec->ftvl > DBF_ENUM)
+            prec->ftvl = DBF_UCHAR;
+        prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
+            "arr calloc failed");
+
+        if (prec->nelm == 1) {
+            prec->nord = 1;
+        } else {
+            prec->nord = 0;
+        }
+        return 0;
+    }
+    return 0;
+}
+
+static long process(arrRecord *prec)
+{
+    return 0;
+}
+
+static long cvt_dbaddr(DBADDR *paddr)
+{
+    arrRecord *prec = (arrRecord *) paddr->precord;
+
+    paddr->pfield = prec->bptr;
+    paddr->no_elements = prec->nelm;
+    paddr->field_type = prec->ftvl;
+    paddr->field_size = dbValueSize(prec->ftvl);
+    paddr->dbr_field_type = prec->ftvl;
+
+    return 0;
+}
+
+static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
+{
+    arrRecord *prec = (arrRecord *) paddr->precord;
+
+    *no_elements = prec->nord;
+    *offset = prec->off;
+
+    return 0;
+}
+
+static long put_array_info(DBADDR *paddr, long nNew)
+{
+    arrRecord *prec = (arrRecord *) paddr->precord;
+
+    prec->nord = nNew;
+    if (prec->nord > prec->nelm)
+        prec->nord = prec->nelm;
+
+    return 0;
+}

=== added file 'src/ioc/db/filters/test/arrRecord.dbd'
--- src/ioc/db/filters/test/arrRecord.dbd	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/arrRecord.dbd	2012-05-30 18:10:27 +0000
@@ -0,0 +1,33 @@
+include "menuGlobal.dbd"
+include "menuConvert.dbd"
+recordtype(arr) {
+  include "dbCommon.dbd"
+  field(VAL, DBF_NOACCESS) {
+    prompt("Value")
+    special(SPC_DBADDR)
+    pp(TRUE)
+    extra("void *val")
+  }
+  field(NELM, DBF_ULONG) {
+    prompt("Number of Elements")
+    special(SPC_NOMOD)
+    initial("1")
+  }
+  field(FTVL, DBF_MENU) {
+    prompt("Field Type of Value")
+    special(SPC_NOMOD)
+    menu(menuFtype)
+  }
+  field(NORD, DBF_ULONG) {
+    prompt("Number elements read")
+    special(SPC_NOMOD)
+  }
+  field(OFF, DBF_ULONG) {
+    prompt("Offset into array")
+  }
+  field(BPTR, DBF_NOACCESS) {
+    prompt("Buffer Pointer")
+    special(SPC_NOMOD)
+    extra("void *bptr")
+  }
+}

=== added file 'src/ioc/db/filters/test/arrTest.cpp'
--- src/ioc/db/filters/test/arrTest.cpp	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/arrTest.cpp	2012-05-30 18:10:27 +0000
@@ -0,0 +1,337 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2003 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to the Software License Agreement
+* found in the file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+/* using stuff from softIoc.cpp by Andrew Johnson */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "registryFunction.h"
+#include "epicsThread.h"
+#include "epicsExit.h"
+#include "epicsStdio.h"
+#include "envDefs.h"
+#include "dbStaticLib.h"
+#include "subRecord.h"
+#include "dbAddr.h"
+#include "dbAccess.h"
+#include "asDbLib.h"
+#include "iocInit.h"
+#include "iocsh.h"
+#include "dbChannel.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+#include "arrRecord.h"
+
+extern "C" int arrRecord_registerRecordDeviceDriver(struct dbBase *pdbbase);
+extern "C" void (*pvar_func_arrInitialize)(void);
+
+#define CA_SERVER_PORT "65535"
+
+#define PATTERN 0x55
+
+const char *server_port = CA_SERVER_PORT;
+
+static void exitSubroutine(subRecord *precord) {
+    epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
+    for (int i = 0; i < pfl1->no_elements; i++) {
+        switch (type) {
+        case DBR_DOUBLE:
+            if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+                testDiag("at index=%d: field log has %g, should be %d",
+                         i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+                return 0;
+            }
+            break;
+        case DBR_LONG:
+            if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+                testDiag("at index=%d: field log has %d, should be %d",
+                         i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+                return 0;
+            }
+            break;
+        case DBR_STRING:
+            if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
+                testDiag("at index=%d: field log has '%s', should be '%d'",
+                         i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
+                return 0;
+            }
+            break;
+        default:
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) {
+    ELLNODE *node;
+    chFilter *filter;
+    char name[80];
+
+    strncpy(name, chan, sizeof(name)-1);
+    strncat(name, json, sizeof(name)-strlen(name)-1);
+
+    testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type);
+    testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no);
+
+    testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened");
+
+    node = ellFirst(&(*pch)->pre_chain);
+    filter = CONTAINER(node, chFilter, pre_node);
+    testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain");
+
+    node = ellFirst(&(*pch)->post_chain);
+    filter = CONTAINER(node, chFilter, post_node);
+    testOk((ellCount(&(*pch)->post_chain) == no),
+           "arr has %d filter(s) in post chain", no);
+}
+
+static void testHead (const char *title, const char *typ = "") {
+    const char *line = "------------------------------------------------------------------------------";
+    testDiag(line);
+    testDiag(title, typ);
+    testDiag(line);
+}
+
+#define TEST1(Size, Offset, Incr, Text) \
+    testDiag("Offset: %d (%s)", Offset, Text); \
+    off = Offset; \
+    status = dbPutField(&offaddr, DBR_LONG, &off, 1); \
+    pfl = db_create_read_log(pch); \
+    testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
+    pfl2 = dbChannelRunPostChain(pch, pfl); \
+    testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
+    testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
+    testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \
+    db_delete_field_log(pfl);
+
+static void check(short dbr_type) {
+    dbChannel *pch;
+    db_field_log *pfl, *pfl2;
+    int status;
+    dbAddr valaddr;
+    dbAddr offaddr;
+    const char *offname = NULL, *valname = NULL, *typname = NULL;
+    long ar[10] = {10,11,12,13,14,15,16,17,18,19};
+    long *ar10_0_1 = ar;
+    long ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13};
+    long ar5_0_1[10] = {12,13,14,15,16};
+    long ar5_3_1[10] = {15,16,17,18,19};
+    long ar5_5_1[10] = {17,18,19,10,11};
+    long ar5_9_1[10] = {11,12,13,14,15};
+    long ar5_0_2[10] = {12,14,16};
+    long ar5_3_2[10] = {15,17,19};
+    long ar5_5_2[10] = {17,19,11};
+    long ar5_9_2[10] = {11,13,15};
+    long ar5_0_3[10] = {12,15};
+    long ar5_3_3[10] = {15,18};
+    long ar5_5_3[10] = {17,10};
+    long ar5_9_3[10] = {11,14};
+    long off = 0;
+
+    switch (dbr_type) {
+    case DBR_LONG:
+        offname = "x.OFF";
+        valname = "x.VAL";
+        typname = "long";
+        break;
+    case DBR_DOUBLE:
+        offname = "y.OFF";
+        valname = "y.VAL";
+        typname = "double";
+        break;
+    case DBR_STRING:
+        offname = "z.OFF";
+        valname = "z.VAL";
+        typname = "string";
+        break;
+    default:
+        testDiag("Invalid data type %d", dbr_type);
+    }
+
+    status = dbNameToAddr(offname, &offaddr);
+
+    status = dbNameToAddr(valname, &valaddr);
+    status = dbPutField(&valaddr, DBR_LONG, ar, 10);
+
+    /* Default: should not change anything */
+
+    testHead("Ten %s elements from rec, increment 1, full size (default)", typname);
+    createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1);
+    testOk(pch->final_type == valaddr.field_type,
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
+    testOk(pch->final_no_elements == valaddr.no_elements,
+           "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
+    TEST1(10, 0, 1, "no offset");
+    TEST1(10, 4, 1, "wrapped");
+    dbChannelDelete(pch);
+
+    testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname);
+    createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1);
+    testOk(pch->final_type == valaddr.field_type,
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
+    testOk(pch->final_no_elements == valaddr.no_elements,
+           "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
+    TEST1(10, 4, 1, "wrapped");
+    dbChannelDelete(pch);
+
+    testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname);
+    createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1);
+    testOk(pch->final_type == valaddr.field_type,
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
+    testOk(pch->final_no_elements == valaddr.no_elements,
+           "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
+    TEST1(10, 4, 1, "wrapped");
+    dbChannelDelete(pch);
+
+    testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname);
+    createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1);
+    testOk(pch->final_type == valaddr.field_type,
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
+    testOk(pch->final_no_elements == valaddr.no_elements,
+           "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
+    TEST1(10, 4, 1, "wrapped");
+    dbChannelDelete(pch);
+
+    testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname);
+    createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1);
+    testOk(pch->final_type == valaddr.field_type,
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
+    testOk(pch->final_no_elements == valaddr.no_elements,
+           "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
+    TEST1(10, 4, 1, "wrapped");
+    dbChannelDelete(pch);
+
+#define TEST5(Incr, Left, Right, Type) \
+    testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \
+    createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
+                  "(" #Left ":" #Incr ":" #Right ")", &pch, 1); \
+    testOk(pch->final_type == valaddr.field_type, \
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
+    testOk(pch->final_no_elements == 4 / Incr + 1, \
+           "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
+    TEST1(5, 0, Incr, "no offset"); \
+    TEST1(5, 3, Incr, "from upper block"); \
+    TEST1(5, 5, Incr, "wrapped"); \
+    TEST1(5, 9, Incr, "from lower block"); \
+    dbChannelDelete(pch);
+
+    /* Contiguous block of 5 */
+
+    TEST5(1,  2,  6, "regular");
+    TEST5(1, -8,  6, "left side from-end");
+    TEST5(1,  2, -4, "right side from-end");
+    TEST5(1, -8, -4, "both sides from-end");
+
+    /* 5 elements with increment 2 */
+
+    TEST5(2,  2,  6, "regular");
+    TEST5(2, -8,  6, "left side from-end");
+    TEST5(2,  2, -4, "right side from-end");
+    TEST5(2, -8, -4, "both sides from-end");
+
+    /* 5 elements with increment 3 */
+
+    TEST5(3,  2,  6, "regular");
+    TEST5(3, -8,  6, "left side from-end");
+    TEST5(3,  2, -4, "right side from-end");
+    TEST5(3, -8, -4, "both sides from-end");
+
+    /* From buffer (plugin chain) */
+
+#define TEST5B(Incr, Left, Right, Type) \
+    testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \
+    createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
+                  "(" #Left ":" #Incr ":" #Right ")", &pch, 2); \
+    testOk(pch->final_type == valaddr.field_type, \
+           "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
+    testOk(pch->final_no_elements == 4 / Incr + 1, \
+           "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
+    TEST1(5, 0, Incr, "no offset"); \
+    dbChannelDelete(pch);
+
+    /* Contiguous block of 5 */
+
+    TEST5B(1,  2,  6, "regular");
+    TEST5B(1, -8,  6, "left side from-end");
+    TEST5B(1,  2, -4, "right side from-end");
+    TEST5B(1, -8, -4, "both sides from-end");
+
+    /* 5 elements with increment 2 */
+
+    TEST5B(2,  2,  6, "regular");
+    TEST5B(2, -8,  6, "left side from-end");
+    TEST5B(2,  2, -4, "right side from-end");
+    TEST5B(2, -8, -4, "both sides from-end");
+
+    /* 5 elements with increment 3 */
+
+    TEST5B(3,  2,  6, "regular");
+    TEST5B(3, -8,  6, "left side from-end");
+    TEST5B(3,  2, -4, "right side from-end");
+    TEST5B(3, -8, -4, "both sides from-end");
+}
+
+MAIN(arrTest)
+{
+    const chFilterPlugin *plug;
+    char arr[] = "arr";
+    int status;
+
+    testPlan(1404);
+
+    /* Prepare the IOC */
+
+    epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
+
+    testOk1(!(status=dbReadDatabase(&pdbbase, "arrRecord.dbd", ".:..:../../../../../../dbd", NULL)));
+    if (status) epicsExit(EXIT_FAILURE);
+
+    (*pvar_func_arrInitialize)();
+    arrRecord_registerRecordDeviceDriver(pdbbase);
+    registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
+
+    testOk1(!(status=dbReadDatabase(&pdbbase, "arrTest.db", ".:..", NULL)));
+    if (status) epicsExit(EXIT_FAILURE);
+
+    /* Start the IOC */
+
+    iocInit();
+    db_init_events();
+    epicsThreadSleep(0.2);
+
+    testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly");
+
+    check(DBR_LONG);
+    check(DBR_DOUBLE);
+    check(DBR_STRING);
+
+    dbFreeBase(pdbbase);
+
+    return testDone();
+
+    epicsExit(EXIT_SUCCESS);
+    /*Note that the following statement will never be executed*/
+    return 0;
+}

=== added file 'src/ioc/db/filters/test/arrTest.db'
--- src/ioc/db/filters/test/arrTest.db	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/arrTest.db	2012-05-30 18:10:27 +0000
@@ -0,0 +1,15 @@
+record(arr, "x") {
+    field(DESC, "test array record")
+    field(NELM, "10")
+    field(FTVL, "LONG")
+}
+record(arr, "y") {
+    field(DESC, "test array record")
+    field(NELM, "10")
+    field(FTVL, "DOUBLE")
+}
+record(arr, "z") {
+    field(DESC, "test array record")
+    field(NELM, "10")
+    field(FTVL, "STRING")
+}

=== added file 'src/ioc/db/filters/test/dbndTest.c'
--- src/ioc/db/filters/test/dbndTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/dbndTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,228 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "db_field_log.h"
+#include "dbCommon.h"
+#include "chfPlugin.h"
+#include "epicsUnitTest.h"
+#include "epicsTime.h"
+#include "testMain.h"
+
+#define PATTERN 0x55
+
+epicsShareExtern void (*pvar_func_dbndInitialize)(void);
+
+static db_field_log fl;
+
+static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
+    return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
+}
+
+static void fl_setup(dbChannel *chan, db_field_log *pfl) {
+    struct dbCommon  *prec = dbChannelRecord(chan);
+
+    pfl->ctx  = dbfl_context_read;
+    pfl->type = dbfl_type_val;
+    pfl->stat = prec->stat;
+    pfl->sevr = prec->sevr;
+    pfl->time = prec->time;
+    pfl->field_type  = dbChannelFieldType(chan);
+    pfl->no_elements = dbChannelElements(chan);
+    /*
+     * use memcpy to avoid a bus error on
+     * union copy of char in the db at an odd
+     * address
+     */
+    memcpy(&pfl->u.v.field,
+           dbChannelField(chan),
+           dbChannelFieldSize(chan));
+}
+
+static void changeValue(db_field_log *pfl2, long val) {
+    pfl2->u.v.field.dbf_long = val;
+    testDiag("new value: %ld", val);
+}
+
+static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+    changeValue(pfl2, val);
+    testDiag("mode=%s delta=%g filter must pass once", m, d);
+    db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
+    testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data");
+    pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(NULL == pfl, "call 2 drops field_log");
+}
+
+static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+    changeValue(pfl2, val);
+    testDiag("mode=%s delta=%g filter must drop", m, d);
+    db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(NULL == pfl, "call 1 drops field_log");
+}
+
+static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+    changeValue(pfl2, val);
+    testDiag("mode=%s delta=%g filter must pass twice", m, d);
+    db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
+    testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data");
+    pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
+    testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data");
+}
+
+static void testHead (char* title) {
+    testDiag("--------------------------------------------------------");
+    testDiag(title);
+    testDiag("--------------------------------------------------------");
+}
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(dbndTest)
+{
+    dbChannel *pch;
+    chFilter *filter;
+    const chFilterPlugin *plug;
+    char dbnd[] = "dbnd";
+    ELLNODE *node;
+    chPostEventFunc *cb_out = NULL;
+    void *arg_out = NULL;
+    db_field_log *pfl2;
+    db_field_log fl1;
+
+    testPlan(62);
+
+    db_init_events();
+
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    (*pvar_func_dbndInitialize)();       /* manually initialize plugin */
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL));
+
+    testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly");
+
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created");
+    testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
+
+    memset(&fl, PATTERN, sizeof(fl));
+    fl1 = fl;
+    node = ellFirst(&pch->filters);
+    filter = CONTAINER(node, chFilter, list_node);
+    plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
+    testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument");
+    testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type");
+
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
+    node = ellFirst(&pch->pre_chain);
+    filter = CONTAINER(node, chFilter, pre_node);
+    testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL),
+           "dbnd has one filter with argument in pre chain");
+    testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
+
+    /* Field logs of type ref and rec: pass any update */
+
+    testHead("Field logs of type ref and rec");
+    fl1.type = dbfl_type_rec;
+    mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
+
+    fl1.type = dbfl_type_ref;
+    mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
+
+    /* Delta = 0: pass any change */
+
+    testHead("Delta = 0: pass any change");
+    pfl2 = db_create_read_log(pch);
+    testDiag("new field_log from record");
+    fl_setup(pch, pfl2);
+
+    mustPassOnce(pch, pfl2, "abs", 0., 0);
+    mustPassOnce(pch, pfl2, "abs", 0., 1);
+
+    db_delete_field_log(pfl2);
+    dbChannelDelete(pch);
+
+    /* Delta = -1: pass any update */
+
+    testHead("Delta = -1: pass any update");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":-1.0}}")), "dbChannel with plugin dbnd (delta=-1) created");
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
+
+    pfl2 = db_create_read_log(pch);
+    testDiag("new field_log from record");
+    fl_setup(pch, pfl2);
+
+    mustPassTwice(pch, pfl2, "abs", -1., 0);
+    mustPassTwice(pch, pfl2, "abs", -1., 1);
+
+    db_delete_field_log(pfl2);
+    dbChannelDelete(pch);
+
+    /* Delta = absolute */
+
+    testHead("Delta = absolute");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":3}}")), "dbChannel with plugin dbnd (delta=3) created");
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
+
+    pfl2 = db_create_read_log(pch);
+    testDiag("new field_log from record");
+    fl_setup(pch, pfl2);
+
+    mustPassOnce(pch, pfl2, "abs", 3., 1);
+    mustDrop(pch, pfl2, "abs", 3., 3);
+    mustDrop(pch, pfl2, "abs", 3., 4);
+    mustPassOnce(pch, pfl2, "abs", 3., 5);
+
+    db_delete_field_log(pfl2);
+    dbChannelDelete(pch);
+
+    /* Delta = relative */
+
+    testHead("Delta = relative");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"m\":\"rel\",\"d\":50}}")),
+           "dbChannel with plugin dbnd (mode=rel, delta=50) created");
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
+
+    pfl2 = db_create_read_log(pch);
+    testDiag("new field_log from record");
+    fl_setup(pch, pfl2);
+
+    mustPassOnce(pch, pfl2, "rel", 50., 1);
+    mustPassOnce(pch, pfl2, "rel", 50., 2);
+    mustDrop(pch, pfl2, "rel", 50., 3);
+    mustPassOnce(pch, pfl2, "rel", 50., 4);
+    mustDrop(pch, pfl2, "rel", 50., 5);
+    mustDrop(pch, pfl2, "rel", 50., 6);
+    mustPassOnce(pch, pfl2, "rel", 50., 7);
+
+    db_delete_field_log(pfl2);
+    dbChannelDelete(pch);
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/filters/test/syncTest.c'
--- src/ioc/db/filters/test/syncTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/syncTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,371 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "db_field_log.h"
+#include "dbCommon.h"
+#include "dbChannel.h"
+#include "chfPlugin.h"
+#include "epicsUnitTest.h"
+#include "epicsTime.h"
+#include "dbState.h"
+#include "testMain.h"
+
+#define PATTERN 0x55
+
+epicsShareExtern void (*pvar_func_syncInitialize)(void);
+
+static db_field_log fl;
+static dbStateId red;
+
+static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
+    return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
+}
+
+static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) {
+    struct dbCommon  *prec = dbChannelRecord(chan);
+
+    pfl->ctx  = dbfl_context_event;
+    pfl->type = dbfl_type_val;
+    pfl->stat = prec->stat;
+    pfl->sevr = prec->sevr;
+    pfl->time = prec->time;
+    pfl->field_type  = DBF_LONG;
+    pfl->no_elements = 1;
+    /*
+     * use memcpy to avoid a bus error on
+     * union copy of char in the db at an odd
+     * address
+     */
+    memcpy(&pfl->u.v.field,
+           dbChannelField(chan),
+           dbChannelFieldSize(chan));
+    pfl->u.v.field.dbf_long = val;
+}
+
+static void changeValue(db_field_log *pfl2, long val) {
+    pfl2->u.v.field.dbf_long = val;
+}
+
+static void testHead (char* title) {
+    testDiag("--------------------------------------------------------");
+    testDiag(title);
+    testDiag("--------------------------------------------------------");
+}
+
+static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) {
+    db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(NULL == pfl, "filter drops field_log (%s)", m);
+}
+
+static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) {
+    testDiag("%s: filter must pass twice", m);
+    db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
+    pfl = dbChannelRunPreChain(pch, pfl2);
+    testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
+}
+
+static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) {
+    db_field_log *pfl = dbChannelRunPreChain(pch, cur);
+    testOk(old == pfl, "filter passes previous field log (%s)", m);
+}
+
+static void mustPass(dbChannel *pch, db_field_log *cur, char* m) {
+    db_field_log *pfl = dbChannelRunPreChain(pch, cur);
+    testOk(cur == pfl, "filter passes field_log (%s)", m);
+}
+
+static void checkCtxRead(dbChannel *pch, dbStateId id) {
+    fl.ctx = dbfl_context_read;
+    dbStateClear(id);
+    mustPassTwice(pch, &fl, "ctx='read', state=FALSE");
+    dbStateSet(id);
+    mustPassTwice(pch, &fl, "ctx='read', state=TRUE");
+    dbStateClear(id);
+    mustPassTwice(pch, &fl, "ctx='read', state=FALSE");
+    fl.ctx = dbfl_context_event;
+}
+
+static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) {
+    ELLNODE *node;
+    chFilter *filter;
+    chPostEventFunc *cb_out = NULL;
+    void *arg_out = NULL;
+
+    testDiag("Test filter structure and open channel");
+
+    testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
+
+    db_field_log fl1 = fl;
+    node = ellFirst(&pch->filters);
+    filter = CONTAINER(node, chFilter, list_node);
+    plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
+    testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument");
+    testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type");
+
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin sync opened");
+    node = ellFirst(&pch->pre_chain);
+    filter = CONTAINER(node, chFilter, pre_node);
+    testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL),
+           "sync has one filter with argument in pre chain");
+    testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain");
+
+    checkCtxRead(pch, red);
+}
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+//MAIN(syncTest)
+int main()
+{
+    dbChannel *pch;
+    chFilter *filter;
+    const chFilterPlugin *plug;
+    char myname[] = "sync";
+    ELLNODE *node;
+    chPostEventFunc *cb_out = NULL;
+    void *arg_out = NULL;
+    db_field_log *pfl[10];
+    db_field_log fl1;
+    int i;
+
+    testPlan(0);
+
+    db_init_events();
+
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    (*pvar_func_syncInitialize)();       /* manually initialize plugin */
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL));
+
+    testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname);
+    testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully");
+
+    /* nonexisting state */
+    testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"blue\"}}")),
+           "dbChannel with sync (m='while' s='blue') (nonex state) failed");
+    /* missing state */
+    testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\"}}")),
+           "dbChannel with sync (m='while') (no state) failed");
+    /* missing mode */
+    testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"s\":\"red\"}}")),
+           "dbChannel with sync (s='red') (no mode) failed");
+
+    /* mode WHILE */
+
+    testHead("Mode WHILE  (m='while', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='while' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustDrop(pch, pfl[0], "state=FALSE, log0");
+    mustDrop(pch, pfl[1], "state=FALSE, log1");
+    mustDrop(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustPass(pch, pfl[3], "state=TRUE, log3");
+    mustPass(pch, pfl[4], "state=TRUE, log4");
+    mustPass(pch, pfl[5], "state=TRUE, log5");
+    dbStateClear(red);
+    mustDrop(pch, pfl[6], "state=FALSE, log6");
+    mustDrop(pch, pfl[7], "state=FALSE, log7");
+    mustDrop(pch, pfl[8], "state=FALSE, log8");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    /* mode UNLESS */
+
+    testHead("Mode UNLESS  (m='unless', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"unless\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='unless' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustPass(pch, pfl[0], "state=FALSE, log0");
+    mustPass(pch, pfl[1], "state=FALSE, log1");
+    mustPass(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustDrop(pch, pfl[3], "state=TRUE, log3");
+    mustDrop(pch, pfl[4], "state=TRUE, log4");
+    mustDrop(pch, pfl[5], "state=TRUE, log5");
+    dbStateClear(red);
+    mustPass(pch, pfl[6], "state=FALSE, log6");
+    mustPass(pch, pfl[7], "state=FALSE, log7");
+    mustPass(pch, pfl[8], "state=FALSE, log8");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    /* mode BEFORE */
+
+    testHead("Mode BEFORE  (m='before', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"before\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='before' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustDrop(pch, pfl[0], "state=FALSE, log0");
+    mustDrop(pch, pfl[1], "state=FALSE, log1");
+    mustDrop(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2");
+    mustDrop(pch, pfl[4], "state=TRUE, log4");
+    mustDrop(pch, pfl[5], "state=TRUE, log5");
+    mustDrop(pch, pfl[6], "state=TRUE, log6");
+    dbStateClear(red);
+    mustDrop(pch, pfl[7], "state=FALSE, log7");
+    mustDrop(pch, pfl[8], "state=FALSE, log8");
+    mustDrop(pch, pfl[9], "state=FALSE, log9");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    /* mode FIRST */
+
+    testHead("Mode FIRST  (m='first', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"first\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='first' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustDrop(pch, pfl[0], "state=FALSE, log0");
+    mustDrop(pch, pfl[1], "state=FALSE, log1");
+    mustDrop(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustPass(pch, pfl[3], "state=TRUE, log3");
+    mustDrop(pch, pfl[4], "state=TRUE, log4");
+    mustDrop(pch, pfl[5], "state=TRUE, log5");
+    dbStateClear(red);
+    mustDrop(pch, pfl[6], "state=FALSE, log6");
+    mustDrop(pch, pfl[7], "state=FALSE, log7");
+    mustDrop(pch, pfl[8], "state=FALSE, log8");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    /* mode LAST */
+
+    testHead("Mode LAST  (m='last', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"last\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='last' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustDrop(pch, pfl[0], "state=FALSE, log0");
+    mustDrop(pch, pfl[1], "state=FALSE, log1");
+    mustDrop(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustDrop(pch, pfl[3], "state=TRUE, log3");
+    mustDrop(pch, pfl[4], "state=TRUE, log4");
+    mustDrop(pch, pfl[5], "state=TRUE, log5");
+    dbStateClear(red);
+    mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5");
+    mustDrop(pch, pfl[7], "state=FALSE, log7");
+    mustDrop(pch, pfl[8], "state=FALSE, log8");
+    mustDrop(pch, pfl[9], "state=FALSE, log9");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    /* mode AFTER */
+
+    testHead("Mode AFTER  (m='after', s='red')");
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"after\",\"s\":\"red\"}}")),
+           "dbChannel with plugin sync (m='after' s='red') created");
+
+    checkAndOpenChannel(pch, plug);
+
+    for (i = 0; i < 10; i++) {
+        pfl[i] = db_create_read_log(pch);
+        fl_setup(pch, pfl[i], 120 + i);
+    }
+
+    testDiag("Test event stream");
+
+    dbStateClear(red);
+    mustDrop(pch, pfl[0], "state=FALSE, log0");
+    mustDrop(pch, pfl[1], "state=FALSE, log1");
+    mustDrop(pch, pfl[2], "state=FALSE, log2");
+    dbStateSet(red);
+    mustDrop(pch, pfl[3], "state=TRUE, log3");
+    mustDrop(pch, pfl[4], "state=TRUE, log4");
+    mustDrop(pch, pfl[5], "state=TRUE, log5");
+    dbStateClear(red);
+    mustPass(pch, pfl[6], "state=FALSE, log6");
+    mustDrop(pch, pfl[7], "state=FALSE, log7");
+    mustDrop(pch, pfl[8], "state=FALSE, log8");
+
+    for (i = 0; i < 10; i++)
+        db_delete_field_log(pfl[i]);
+
+    dbChannelDelete(pch);
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/filters/test/tsTest.c'
--- src/ioc/db/filters/test/tsTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/test/tsTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,111 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "chfPlugin.h"
+#include "epicsUnitTest.h"
+#include "epicsTime.h"
+#include "testMain.h"
+
+#define PATTERN 0x55
+
+epicsShareExtern void (*pvar_func_tsInitialize)(void);
+
+static db_field_log fl;
+
+static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
+    return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
+}
+
+static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) {
+    db_field_log fl1 = *pfl1;
+
+    fl1.time = pfl2->time;
+    return fl_equal(&fl1, pfl2);
+}
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(tsTest)
+{
+    dbChannel *pch;
+    chFilter *filter;
+    const chFilterPlugin *plug;
+    char ts[] = "ts";
+    ELLNODE *node;
+    chPostEventFunc *cb_out = NULL;
+    void *arg_out = NULL;
+    db_field_log fl1;
+    db_field_log *pfl2;
+
+    testPlan(15);
+
+    db_init_events();
+
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    (*pvar_func_tsInitialize)();       /* manually initialize plugin */
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL));
+
+    testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly");
+
+    testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created");
+    testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
+
+    memset(&fl, PATTERN, sizeof(fl));
+    fl1 = fl;
+    node = ellFirst(&pch->filters);
+    filter = CONTAINER(node, chFilter, list_node);
+    plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
+    testOk(!!(cb_out) && !(arg_out), "register_pre registers one filter w/o argument");
+    testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type");
+
+    testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened");
+    node = ellFirst(&pch->pre_chain);
+    filter = CONTAINER(node, chFilter, pre_node);
+    testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL),
+           "ts has one filter w/o argument in pre chain");
+    testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain");
+
+    memset(&fl, PATTERN, sizeof(fl));
+    fl1 = fl;
+    pfl2 = dbChannelRunPreChain(pch, &fl1);
+    testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log");
+    testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data");
+
+    testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel");
+    epicsTimeStamp stamp = pfl2->time;
+    pfl2 = dbChannelRunPreChain(pch, &fl1);
+    epicsTimeStamp now; epicsTimeGetCurrent(&now);
+    testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) > 0. &&
+           epicsTimeDiffInSeconds(&now, &pfl2->time) > 0., "ts filter sets time stamp to \"now\"");
+
+    dbChannelDelete(pch);
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/filters/ts.c'
--- src/ioc/db/filters/ts.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/filters/ts.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,67 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <stdio.h>
+
+#include <epicsExport.h>
+#include <chfPlugin.h>
+#include <db_field_log.h>
+
+static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
+    epicsTimeStamp now;
+    epicsTimeGetCurrent(&now);
+
+    /* If string or array, must make a copy (to ensure coherence between time and data) */
+    if (pfl->type == dbfl_type_rec) {
+        dbChannelMakeArrayCopy(pvt, pfl, chan);
+    }
+
+    pfl->time = now;
+    return pfl;
+}
+
+static void channelRegisterPre(dbChannel *chan, void *pvt,
+                               chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    *cb_out = filter;
+}
+
+static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
+{
+    printf("%*s  plugin ts\n", indent, "");
+}
+
+static chfPluginIf pif = {
+    NULL, /* allocPvt, */
+    NULL, /* freePvt, */
+
+    NULL, /* parse_error, */
+    NULL, /* parse_ok, */
+
+    NULL, /* channel_open, */
+    channelRegisterPre,
+    NULL, /* channelRegisterPost, */
+    channel_report,
+    NULL /* channel_close */
+};
+
+static void tsInitialize(void)
+{
+    static int firstTime = 1;
+
+    if(!firstTime) return;
+    firstTime = 0;
+
+    chfPluginRegister("ts", &pif, NULL);
+}
+
+epicsExportRegistrar(tsInitialize);

=== modified file 'src/ioc/db/recGbl.c'
--- src/ioc/db/recGbl.c	2010-10-05 19:27:37 +0000
+++ src/ioc/db/recGbl.c	2012-05-30 18:10:27 +0000
@@ -6,12 +6,10 @@
 * EPICS BASE is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution. 
 \*************************************************************************/
-/* recGbl.c */
-/* $Revision-Id$ */
 
 /*
  *      Author:          Marty Kraimer
- *      Date:            11-7-90
+ *                       Andrew Johnson <[email protected]>
  */
 
 #include <stddef.h>
@@ -34,6 +32,7 @@
 #include "caeventmask.h"
 #define epicsExportSharedSymbols
 #include "dbAccessDefs.h"
+#include "dbLink.h"
 #include "dbNotify.h"
 #include "dbCa.h"
 #include "dbEvent.h"
@@ -166,60 +165,17 @@
 
     return;
 }
-
+
 int  epicsShareAPI recGblInitConstantLink(
     struct link *plink,short dbftype,void *pdest)
 {
-    if(plink->type != CONSTANT) return(FALSE);
-    if(!plink->value.constantStr) return(FALSE);
-    switch(dbftype) {
-    case DBF_STRING:
-	strcpy((char *)pdest,plink->value.constantStr);
-	break;
-    case DBF_CHAR : {
-	epicsInt16 value;
-	epicsInt8 *pvalue = (epicsInt8 *)pdest;
-
-	sscanf(plink->value.constantStr,"%hi",&value);
-	*pvalue = value;
-	}
-	break;
-    case DBF_UCHAR : {
-	epicsUInt16 value;
-	epicsUInt8 *pvalue = (epicsUInt8 *)pdest;
-
-	sscanf(plink->value.constantStr,"%hu",&value);
-	*pvalue = value;
-	}
-	break;
-    case DBF_SHORT : 
-	sscanf(plink->value.constantStr,"%hi",(epicsInt16 *)pdest);
-	break;
-    case DBF_USHORT : 
-    case DBF_ENUM : 
-    case DBF_MENU : 
-    case DBF_DEVICE : 
-	sscanf(plink->value.constantStr,"%hu",(epicsUInt16 *)pdest);
-	break;
-    case DBF_LONG : 
-	*(epicsInt32 *)pdest = strtol(plink->value.constantStr, NULL, 0);
-	break;
-    case DBF_ULONG : 
-	*(epicsUInt32 *)pdest = strtoul(plink->value.constantStr, NULL, 10);
-	break;
-    case DBF_FLOAT : 
-	epicsScanFloat(plink->value.constantStr, (epicsFloat32 *)pdest);
-	break;
-    case DBF_DOUBLE : 
-	epicsScanDouble(plink->value.constantStr, (epicsFloat64 *)pdest);
-	break;
-    default:
-	epicsPrintf("Error in recGblInitConstantLink: Illegal DBF type\n");
-	return(FALSE);
-    }
-    return(TRUE);
+    long status = dbLoadLink(plink, dbftype, pdest);
+
+    if (status)
+        return FALSE;
+    return TRUE;
 }
-
+
 unsigned short epicsShareAPI recGblResetAlarms(void *precord)
 {
     dbCommon *pdbc = precord;

=== modified file 'src/ioc/db/test/Makefile'
--- src/ioc/db/test/Makefile	2012-05-29 21:20:17 +0000
+++ src/ioc/db/test/Makefile	2012-05-30 18:10:27 +0000
@@ -27,7 +27,31 @@
 TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests
 TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests
 
+TESTPROD_HOST += dbChannelTest
+dbChannelTest_SRCS += dbChannelTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += dbChannelTest
+TESTS += dbChannelTest
+
+TESTPROD_HOST += chfPluginTest
+chfPluginTest_SRCS += chfPluginTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += chfPluginTest
+TESTS += chfPluginTest
+
+TESTPROD_HOST += arrShorthandTest
+arrShorthandTest_SRCS += arrShorthandTest.c xRecord_registerRecordDeviceDriver.cpp
+OBJS_IOC_vxWorks += arrShorthandTest
+TESTS += arrShorthandTest
+
+TESTPROD_HOST += dbStateTest
+dbStateTest_SRCS += dbStateTest.c
+OBJS_IOC_vxWorks += dbStateTest
+TESTS += dbStateTest
+
 TESTSCRIPTS_HOST += $(TESTS:%=%.t)
 
 include $(TOP)/configure/RULES
 
+dbChannelTest$(OBJ): $(COMMON_DIR)/xRecord.h
+chfPluginTest$(OBJ): $(COMMON_DIR)/xRecord.h
+arrShorthandTest$(OBJ): $(COMMON_DIR)/xRecord.h
+dbStateTest$(OBJ): $(COMMON_DIR)/xRecord.h

=== added file 'src/ioc/db/test/arrShorthandTest.c'
--- src/ioc/db/test/arrShorthandTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/arrShorthandTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,139 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+ /*
+  * Test the shorthand array notation  [ start : incr : end ]
+  * by registering a thin fake arr plugin
+  * and checking if values are forwarded correctly
+  */
+
+#include <string.h>
+
+#include "chfPlugin.h"
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+typedef struct myStruct {
+    epicsInt32 start;
+    epicsInt32 incr;
+    epicsInt32 end;
+} myStruct;
+
+static const
+chfPluginArgDef opts[] = {
+    chfInt32 (myStruct, start, "s", 0, 1),
+    chfInt32 (myStruct, incr, "i", 0, 1),
+    chfInt32 (myStruct, end, "e", 0, 1),
+    chfPluginArgEnd
+};
+
+static myStruct my;
+
+static void * allocPvt(void)
+{
+    my.start = 0;
+    my.incr = 1;
+    my.end = -1;
+    return &my;
+}
+
+static chfPluginIf myPif = {
+    allocPvt,
+    NULL, /* freePvt, */
+
+    NULL, /* parse_error, */
+    NULL, /* parse_ok, */
+
+    NULL, /* channel_open, */
+    NULL, /* channelRegisterPre, */
+    NULL, /* channelRegisterPost, */
+    NULL, /* channel_report, */
+    NULL  /* channel_close */
+};
+
+static int checkValues(epicsUInt32 s, epicsUInt32 i, epicsUInt32 e) {
+    if (s == my.start && i == my.incr && e == my.end)
+        return 1;
+    else
+        return 0;
+}
+
+static void testHead (char* title) {
+    testDiag("--------------------------------------------------------");
+    testDiag(title);
+    testDiag("--------------------------------------------------------");
+}
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(chfPluginTest)
+{
+    dbChannel *pch;
+
+    testPlan(29);
+
+    db_init_events();
+
+    testHead("Set up database");
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL));
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    testHead("Register plugin");
+    testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin");
+
+#define TESTBAD(Title, Expr) \
+    testDiag(Title); \
+    testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \
+    if (pch) dbChannelDelete(pch);
+
+#define TESTGOOD(Title, Expr, Start, Incr, End) \
+    testDiag(Title); \
+    testOk(!!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ")"); \
+    testOk(checkValues(Start, Incr, End), "parameters set correctly: s=%d i=%d e=%d", Start, Incr, End); \
+    if (pch) dbChannelDelete(pch);
+
+    TESTBAD("no parameters []", "[]");
+    TESTBAD("invalid char at beginning [x", "[x");
+    TESTBAD("invalid char after 1st arg [2x", "[2x");
+    TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x");
+    TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x");
+
+    TESTGOOD("one element [index]",         "[2]",     2, 1, 2);
+    TESTGOOD("to end [s:]",                 "[2:]",    2, 1, -1);
+    TESTGOOD("to end [s::]",                "[2::]",   2, 1, -1);
+    TESTGOOD("to end with incr [s:i:]",     "[2:3:]",  2, 3, -1);
+    TESTGOOD("from beginning [:e]",         "[:2]",    0, 1, 2);
+    TESTGOOD("from beginning [::e]",        "[::2]",   0, 1, 2);
+    TESTGOOD("from begin with incr [:i:e]", "[:3:2]",  0, 3, 2);
+    TESTGOOD("range [s:e]",                 "[2:4]",   2, 1, 4);
+    TESTGOOD("range [s::e]",                "[2::4]",  2, 1, 4);
+    TESTGOOD("range with incr [s:i:e]",     "[2:3:4]", 2, 3, 4);
+
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/test/chfPluginTest.c'
--- src/ioc/db/test/chfPluginTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/chfPluginTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,746 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+
+#include "chfPlugin.h"
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+#define PATTERN 0x55555555
+#define TYPE_START 0xAAA
+#define R_LEVEL 42
+
+/* Expected / actually run callback bit definitions */
+#define e_alloc         0x00000001
+#define e_free          0x00000002
+#define e_error         0x00000004
+#define e_ok            0x00000008
+#define e_open          0x00000010
+#define e_reg_pre       0x00000020
+#define e_reg_post      0x00000040
+#define e_report        0x00000080
+#define e_close         0x00000100
+#define e_pre           0x00000200
+#define e_post          0x00000400
+#define e_dtor          0x00000800
+
+unsigned int e1, e2, c1, c2;
+unsigned int offset;
+int drop = -1;
+db_field_log *dtorpfl;
+
+#define e_any (e_alloc | e_free | e_error | e_ok | e_open \
+| e_reg_pre | e_reg_post | e_pre | e_post | e_dtor | e_report | e_close)
+
+typedef struct myStruct {
+    int sent1;
+    char        flag;
+    int sent2;
+    epicsUInt32 ival;
+    int sent3;
+    double      dval;
+    int sent4;
+    int         enumval;
+    int sent5;
+    char        str[20];
+    int sent6;
+    char        c;
+    char        c1[2];
+    int         offpre;
+    int         offpost;
+} myStruct;
+
+static const
+chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} };
+
+static const
+chfPluginArgDef strictOpts[] = {
+    chfInt32  (myStruct, ival,    "i" , 1, 0),
+    chfBoolean(myStruct, flag,    "f" , 1, 0),
+    chfDouble (myStruct, dval,    "d" , 1, 0),
+    chfString (myStruct, str,     "s" , 1, 0),
+    chfEnum   (myStruct, enumval, "c" , 1, 0, colorEnum),
+    chfPluginArgEnd
+};
+
+static const
+chfPluginArgDef noconvOpts[] = {
+    chfInt32  (myStruct, ival,    "i" , 0, 0),
+    chfBoolean(myStruct, flag,    "f" , 0, 0),
+    chfDouble (myStruct, dval,    "d" , 0, 0),
+    chfString (myStruct, str,     "s" , 0, 0),
+    chfEnum   (myStruct, enumval, "c" , 0, 0, colorEnum),
+    chfPluginArgEnd
+};
+
+static const
+chfPluginArgDef sloppyOpts[] = {
+    chfInt32  (myStruct, ival,    "i" , 0, 1),
+    chfBoolean(myStruct, flag,    "f" , 0, 1),
+    chfDouble (myStruct, dval,    "d" , 0, 1),
+    chfString (myStruct, str,     "s" , 0, 1),
+    chfEnum   (myStruct, enumval, "c" , 0, 1, colorEnum),
+    chfPluginArgEnd
+};
+
+/* Options defs with not enough room provided */
+static const
+chfPluginArgDef brokenOpts1[] = {
+    chfInt32 (myStruct,  c1, "i" , 0, 1),
+    chfPluginArgEnd
+};
+
+static const
+chfPluginArgDef brokenOpts2[] = {
+    chfDouble(myStruct,  c1, "d" , 0, 1),
+    chfPluginArgEnd
+};
+
+static const
+chfPluginArgDef brokenOpts3[] = {
+    chfString(myStruct, c1, "s" , 0, 1),
+    chfPluginArgEnd
+};
+
+static const
+chfPluginArgDef brokenOpts4[] = {
+    chfEnum  (myStruct,  c1, "c" , 0, 1, colorEnum),
+    chfPluginArgEnd
+};
+
+int p_ok_return = 0;
+int c_open_return = 0;
+void *puser1, *puser2;
+
+static void clearStruct(void *p) {
+    myStruct *my = (myStruct*) p;
+
+    if (!my) return;
+    memset(my, 0, sizeof(myStruct));
+    my->sent1 = my->sent2 = my->sent3 = my->sent4 = my->sent5 = my->sent6 = PATTERN;
+    my->ival = 12;
+    my->flag = 1;
+    my->dval = 1.234e5;
+    strcpy(my->str, "hello");
+    my->enumval = 4;
+}
+
+static char inst(void* user) {
+    return user == puser1 ? '1' : user == puser2 ? '2' : 'x';
+}
+
+static void * allocPvt(void)
+{
+    myStruct *my = (myStruct*) calloc(1, sizeof(myStruct));
+
+    if (!puser1) {
+        puser1 = my;
+        testOk(e1 & e_alloc, "allocPvt (1) called");
+        c1 |= e_alloc;
+    } else if (!puser2) {
+        puser2 = my;
+        testOk(e2 & e_alloc, "allocPvt (2) called");
+        c2 |= e_alloc;
+    }
+    clearStruct (my);
+    return my;
+}
+
+static void freePvt(void *user)
+{
+    testOk(user == puser1 || user == puser2, "freePvt: user pointer valid");
+    if (user == puser1) {
+        testOk(e1 & e_free, "freePvt (1) called");
+        c1 |= e_free;
+        free(user);
+        puser1 = NULL;
+    } else if (user == puser2) {
+        testOk(e2 & e_free, "freePvt (2) called");
+        c2 |= e_free;
+        free(user);
+        puser2 = NULL;
+    }
+}
+
+static void parse_error(void *user)
+{
+    testOk(user == puser1 || user == puser2, "parse_error: user pointer valid");
+    if (user == puser1) {
+        testOk(e1 & e_error, "parse_error (1) called");
+        c1 |= e_error;
+    } else if (user == puser2) {
+        testOk(e2 & e_error, "parse_error (2) called");
+        c2 |= e_error;
+    }
+}
+
+static int parse_ok(void *user)
+{
+    testOk(user == puser1 || user == puser2, "parse_ok: user pointer valid");
+    if (user == puser1) {
+        testOk(e1 & e_ok, "parse_ok (1) called");
+        c1 |= e_ok;
+    } else if (user == puser2) {
+        testOk(e2 & e_ok, "parse_ok (2) called");
+        c2 |= e_ok;
+
+    }
+
+    return p_ok_return;
+}
+
+static long channel_open(dbChannel *chan, void *user)
+{
+    testOk(user == puser1 || user == puser2, "channel_open: user pointer valid");
+    if (user == puser1) {
+        testOk(e1 & e_open, "channel_open (1) called");
+        c1 |= e_open;
+    } else if (user == puser2) {
+        testOk(e2 & e_open, "channel_open (2) called");
+        c2 |= e_open;
+    }
+
+    return c_open_return;
+}
+
+static void dbfl_free1(db_field_log *pfl) {
+    testOk(e1 & e_dtor, "dbfl_free (1) called");
+    testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct");
+    dtorpfl = NULL;
+    c1 |= e_dtor;
+}
+
+static void dbfl_free2(db_field_log *pfl) {
+    testOk(e2 & e_dtor, "dbfl_free (2) called");
+    testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct");
+    dtorpfl = NULL;
+    c2 |= e_dtor;
+}
+
+static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) {
+    myStruct *my = (myStruct*)user;
+    dbfl_freeFunc *dtor = NULL;
+
+    testOk(user == puser1 || user == puser2, "pre: user pointer valid");
+    if (my == puser1) {
+        testOk(e1 & e_pre, "pre (1) called");
+        testOk(!(c2 & e_pre), "pre (2) has not been called before pre (1)");
+        c1 |= e_pre;
+        dtor = dbfl_free1;
+    } else if (my == puser2) {
+        testOk(e2 & e_pre, "pre (2) called");
+        testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before pre (2)");
+        c2 |= e_pre;
+        dtor = dbfl_free2;
+    }
+    testOk(!(c1 & e_post), "post (1) has not been called before pre (%c)", inst(user));
+    testOk(!(c2 & e_post), "post (2) has not been called before pre (%c)", inst(user));
+
+    if (!testOk(pLog->field_type == TYPE_START + my->offpre, "pre (%c) got field log of expected type", inst(user)))
+        testDiag("expected: %d, got %d", TYPE_START + my->offpre, pLog->field_type);
+    pLog->field_type++;
+
+    if (my->offpre == 0) {                 /* The first one registers a dtor and saves pfl */
+        pLog->u.r.dtor = dtor;
+        dtorpfl = pLog;
+    }
+
+    if (my->offpre == drop) {
+        testDiag("pre (%c) is dropping the field log", inst(user));
+        return NULL;
+    }
+    return pLog;
+}
+
+static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) {
+    myStruct *my = (myStruct*)user;
+    dbfl_freeFunc *dtor = NULL;
+
+    testOk(user == puser1 || user == puser2, "post: user pointer valid");
+    if (my == puser1) {
+        testOk(e1 & e_post, "post (1) called");
+        testOk(!(c2 & e_post), "post (2) has not been called before post (1)");
+        c1 |= e_post;
+        dtor = dbfl_free1;
+    } else if (my == puser2) {
+        testOk(e2 & e_post, "post (2) called");
+        testOk(!(e1 & e_post) || c1 & e_post, "post (1) has been called before post (2)");
+        c2 |= e_post;
+        dtor = dbfl_free2;
+    }
+    testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before post (%c)", inst(user));
+    testOk(!(e2 & e_pre) || c2 & e_pre, "pre (2) has been called before post (%c)", inst(user));
+
+    if (!testOk(pLog->field_type == TYPE_START + my->offpost, "post (%c) got field log of expected type", inst(user)))
+        testDiag("expected: %d, got %d", TYPE_START + my->offpost, pLog->field_type);
+    pLog->field_type++;
+
+    if (my->offpost == 0) {                 /* The first one registers a dtor and remembers pfl */
+        pLog->u.r.dtor = dtor;
+        dtorpfl = pLog;
+    }
+
+    if (my->offpost == drop) {
+        testDiag("post (%c) is dropping the field log", inst(user));
+        return NULL;
+    }
+    return pLog;
+}
+
+static void channelRegisterPre(dbChannel *chan, void *user,
+                               chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    myStruct *my = (myStruct*)user;
+
+    testOk(user == puser1 || user == puser2, "register_pre: user pointer valid");
+    if (my == puser1) {
+        testOk(e1 & e_reg_pre, "register_pre (1) called");
+        testOk(!(c2 & e_reg_pre), "register_pre (2) has not been called before register_pre (1)");
+        c1 |= e_reg_pre;
+    } else if (my == puser2) {
+        testOk(e2 & e_reg_pre, "register_pre (2) called");
+        testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_pre (2)");
+        c2 |= e_reg_pre;
+    }
+    testOk(!(c1 & e_reg_post), "register_post (1) has not been called before register_pre (%c)", inst(user));
+    testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_pre (%c)", inst(user));
+
+    my->offpre = offset++;
+    probe->field_type++;
+    *cb_out  = pre;
+    *arg_out = user;
+}
+
+static void channelRegisterPost(dbChannel *chan, void *user,
+                                chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    myStruct *my = (myStruct*)user;
+
+    testOk(user == puser1 || user == puser2, "register_post: user pointer valid");
+    if (my == puser1) {
+        testOk(e1 & e_reg_post, "register_post (1) called");
+        testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_post (1)");
+        c1 |= e_reg_post;
+    } else if (my == puser2) {
+        testOk(e2 & e_reg_post, "register_post (2) called");
+        testOk(!(e1 & e_reg_post) || c1 & e_reg_post, "register_post (1) has been called before register_post (2)");
+        c2 |= e_reg_post;
+    }
+    testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_post (%c)", inst(user));
+    testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, "register_pre (2) has been called before register_post (%c)", inst(user));
+
+    my->offpost = offset++;
+    probe->field_type++;
+    *cb_out  = post;
+    *arg_out = user;
+}
+
+static void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent)
+{
+    testOk(user == puser1 || user == puser2, "channel_report: user pointer valid");
+    testOk(level == R_LEVEL - 1, "channel_report: level correct");
+    if (user == puser1) {
+        testOk(e1 & e_report, "channel_report (1) called");
+        c1 |= e_report;
+    } else if (user == puser2) {
+        testOk(e2 & e_report, "channel_report (2) called");
+        c2 |= e_report;
+    }
+}
+
+static void channel_close(dbChannel *chan, void *user)
+{
+    testOk(user == puser1 || user == puser2, "channel_close: user pointer valid");
+    if (user == puser1) {
+        testOk(e1 & e_close, "channel_close (1) called");
+        c1 |= e_close;
+    } else if (user == puser2) {
+        testOk(e2 & e_close, "channel_close (2) called");
+        c2 |= e_close;
+    }
+}
+
+static chfPluginIf myPif = {
+    allocPvt,
+    freePvt,
+
+    parse_error,
+    parse_ok,
+
+    channel_open,
+    channelRegisterPre,
+    channelRegisterPost,
+    channel_report,
+    channel_close
+};
+
+static chfPluginIf prePif = {
+    allocPvt,
+    freePvt,
+
+    parse_error,
+    parse_ok,
+
+    channel_open,
+    channelRegisterPre,
+    NULL,
+    channel_report,
+    channel_close
+};
+
+static chfPluginIf postPif = {
+    allocPvt,
+    freePvt,
+
+    parse_error,
+    parse_ok,
+
+    channel_open,
+    NULL,
+    channelRegisterPost,
+    channel_report,
+    channel_close
+};
+
+static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) {
+    if (!my) return 0;
+    if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN
+        && my->sent4 == PATTERN && my->sent5 == PATTERN && my->sent6 == PATTERN
+        && my->ival == i && my->flag == f && my->dval == d && my->enumval == c
+        && (strcmp(s, my->str) == 0)) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static void testHead (char* title) {
+    testDiag("--------------------------------------------------------");
+    testDiag(title);
+    testDiag("--------------------------------------------------------");
+}
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(chfPluginTest)
+{
+    dbChannel *pch;
+    db_field_log *pfl;
+
+    testPlan(1755);
+
+    db_init_events();
+
+    /* Enum to string conversion */
+    testHead("Enum to string conversion");
+    testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R");
+    testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G");
+    testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B");
+    testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index");
+
+    testHead("Set up database");
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL));
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    testHead("Try to register buggy plugins");
+    testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer");
+    testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double");
+    testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string");
+    testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum");
+
+    testHead("Register plugins");
+    testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict");
+    testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv");
+    testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy");
+    testOk(!chfPluginRegister("pre",   &prePif, sloppyOpts), "register plugin pre");
+    testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post");
+
+    /* STRICT parsing: mandatory, no conversion */
+
+    /* All perfect */
+    testHead("STRICT parsing: all ok");
+    e1 = e_alloc | e_ok; c1 = 0;
+    testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct");
+    testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_close | e_free; c1 = 0;
+    if (pch) dbChannelDelete(pch);
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+    /* Any one missing must fail */
+    testHead("STRICT parsing: any missing parameter must fail");
+    e1 = e_alloc | e_error | e_free; c1 = 0;
+    testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "strict parsing: c missing");
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_alloc | e_error | e_free; c1 = 0;
+    testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "strict parsing: s missing");
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_alloc | e_error | e_free; c1 = 0;
+    testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "strict parsing: d missing");
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_alloc | e_error | e_free; c1 = 0;
+    testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "strict parsing: f missing");
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_alloc | e_error | e_free; c1 = 0;
+    testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "strict parsing: i missing");
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+    /* NOCONV parsing: optional, no conversion */
+
+    /* Any one missing must leave the default intact */
+    testHead("NOCONV parsing: any missing parameter must fall back to default value");
+    e1 = e_alloc | e_ok; c1 = 0;
+    testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "noconv parsing: c missing");
+    testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+    e1 = e_close | e_free; c1 = 0;
+    if (pch) dbChannelDelete(pch);
+    testOk(!puser1, "user part cleaned up");
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+    e1 = e_any;
+    testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "noconv parsing: s missing");
+    testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct");
+    if (pch) dbChannelDelete(pch);
+
+    testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: d missing");
+    testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct");
+    if (pch) dbChannelDelete(pch);
+
+    testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: f missing");
+    testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct");
+    if (pch) dbChannelDelete(pch);
+
+    testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: i missing");
+    testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct");
+    if (pch) dbChannelDelete(pch);
+
+    /* Reject wrong types */
+#define WRONGTYPETEST(Var, Val, Typ) \
+    e1 = e_alloc | e_error | e_free; c1 = 0; \
+    testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), "noconv parsing: wrong type "#Typ" for "#Var); \
+    testOk(!puser1, "user part cleaned up"); \
+    if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+    testHead("NOCONV parsing: rejection of wrong parameter types");
+
+    WRONGTYPETEST(i, 123.0, double);
+    WRONGTYPETEST(i, true, boolean);
+    WRONGTYPETEST(i, "1", string);
+    WRONGTYPETEST(f, "false", string);
+    WRONGTYPETEST(f, 0.0, double);
+    WRONGTYPETEST(f, 1, integer);
+    WRONGTYPETEST(d, "1.2", string);
+    WRONGTYPETEST(d, true, boolean);
+    WRONGTYPETEST(d, 123, integer);
+    WRONGTYPETEST(s, 1.23, double);
+    WRONGTYPETEST(s, true, boolean);
+    WRONGTYPETEST(s, 123, integer);
+    WRONGTYPETEST(c, 1.23, double);
+    WRONGTYPETEST(c, true, boolean);
+    WRONGTYPETEST(c, 2, integer);
+
+    /* SLOPPY parsing: optional, with conversion */
+
+#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \
+    e1 = e_alloc | e_ok; c1 = 0; \
+    testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (good) for "#Var); \
+    testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \
+    if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    e1 = e_close | e_free; c1 = 0; \
+    if (pch) dbChannelDelete(pch); \
+    testOk(!puser1, "user part cleaned up"); \
+    if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+#define CONVTESTBAD(Var, Val, Typ) \
+    e1 = e_alloc | e_error | e_free; c1 = 0; \
+    testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (bad) for "#Var); \
+    testOk(!puser1, "user part cleaned up"); \
+    if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+    /* To integer */
+    testHead("SLOPPY parsing: conversion to integer");
+    CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 4);
+    CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 4);
+    CONVTESTBAD(i, "9234567890", out-of-range string);
+    CONVTESTBAD(i, ".4", invalid string);
+    CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 4);
+    CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 4);
+    CONVTESTBAD(i, 34.7e14, out-of-range double);
+
+    /* To boolean */
+    testHead("SLOPPY parsing: conversion to boolean");
+    CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4);    /* Any one missing must leave the default intact */
+    CONVTESTBAD(f, ".4", invalid .4 string);
+    CONVTESTBAD(f, "Flase", misspelled invalid string);
+    CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 4);
+    CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 4);
+
+    /* To double */
+    testHead("SLOPPY parsing: conversion to double");
+    CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 4);
+    CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 4);
+    CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 4);
+    CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 4);
+    CONVTESTBAD(d, "1.67e407", out-of-range double string);
+    CONVTESTBAD(d, "blubb", invalid blubb string);
+    CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 4);
+    CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 4);
+    CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 4);
+    CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 4);
+
+    /* To string */
+    testHead("SLOPPY parsing: conversion to string");
+    CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 4);
+    CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 4);
+    CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 4);
+    CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 4);
+    CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 4);
+    CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", 4);
+    CONVTESTGOOD(s, -1.23456789123456789e26, large rounded negative double, 12, 1, 1.234e5, "-1.234567891235e+26", 4);
+
+    /* To Enum */
+    testHead("SLOPPY parsing: conversion to enum");
+    CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 2);
+    CONVTESTBAD(c, 3, invalid integer choice);
+    CONVTESTBAD(c, 3.2, double);
+    CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 1);
+    CONVTESTBAD(c, "blubb", invalid string choice);
+
+    /* Registering and running filter callbacks */
+
+#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \
+    testHead("Filter chain test, "Type" filter"); \
+    offset = 0; \
+    e1 = e_alloc | e_ok; c1 = 0; \
+    testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \
+    if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    e1 = e_open | ExpReg; c1 = 0; \
+    testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \
+    if (!testOk(c1 == e1, "open channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    e1 = ExpRun; c1 = 0; \
+    testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \
+    pfl->type = dbfl_type_ref; \
+    pfl->field_type = TYPE_START; \
+    testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \
+    testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \
+    testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \
+    db_delete_field_log(pfl); \
+    if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    e1 = e_report; c1 = 0; \
+    dbChannelShow(pch, R_LEVEL, 0); \
+    if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    e1 = e_close | e_free; c1 = 0; \
+    if (pch) dbChannelDelete(pch); \
+    if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1);
+
+#define CHAINTEST2(Type, Json, ExpReg1, ExpRun1, ExpReg2, ExpRun2, DType) \
+    testHead("Filter chain test, "Type" filters"); \
+    offset = 0; \
+    e1 = e_alloc | e_ok; c1 = 0; \
+    e2 = e_alloc | e_ok; c2 = 0; \
+    testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filters"); \
+    if (!testOk(c1 == e1, "create channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    if (!testOk(c2 == e2, "create channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
+    e1 = e_open | ExpReg1; c1 = 0; \
+    e2 = e_open | ExpReg2; c2 = 0; \
+    if (pch) testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \
+    if (!testOk(c1 == e1, "open channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    if (!testOk(c2 == e2, "open channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
+    e1 = ExpRun1; c1 = 0; \
+    e2 = ExpRun2; c2 = 0; \
+    if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \
+    pfl->type = dbfl_type_ref; \
+    pfl->field_type = TYPE_START; \
+    if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)) || (drop >=0 && drop <= 1), "run pre eventq chain"); \
+    if (pch && (drop < 0 || drop >= 2)) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)) || drop >=2, "run post eventq chain"); \
+    if (pfl) testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \
+    if (pfl) db_delete_field_log(pfl); \
+    if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
+    e1 = e_report; c1 = 0; \
+    e2 = e_report; c2 = 0; \
+    dbChannelShow(pch, R_LEVEL, 0); \
+    if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \
+    e1 = e_close | e_free; c1 = 0; \
+    e2 = e_close | e_free; c2 = 0; \
+    if (pch) dbChannelDelete(pch); \
+    if (!testOk(c1 == e1, "delete channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \
+    if (!testOk(c2 == e2, "delete channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2);
+
+    CHAINTEST1("1 pre", "{\"pre\":{}}", e_reg_pre, e_pre | e_dtor, 1);                                        /* One filter, pre chain */
+    CHAINTEST1("1 post", "{\"post\":{}}", e_reg_post, e_post | e_dtor, 1);                                   /* One filter, post chain */
+    CHAINTEST1("1 both", "{\"sloppy\":{}}", e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 2);                   /* One, both chains */
+    CHAINTEST2("2 pre", "{\"pre\":{},\"pre\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_pre, e_pre, 2);          /* Two filters, pre chain */
+    CHAINTEST2("2 post", "{\"post\":{},\"post\":{}}", e_reg_post, e_post | e_dtor, e_reg_post, e_post, 2);  /* Two filters, post chain */
+    CHAINTEST2("2 both", "{\"sloppy\":{},\"sloppy\":{}}",                                                          /* Two, both chains */
+               e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 4);
+    CHAINTEST2("1 pre, 1 post", "{\"pre\":{},\"post\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_post, e_post, 2);   /* Two, pre then post */
+    CHAINTEST2("1 post, 1 pre", "{\"post\":{},\"pre\":{}}", e_reg_post, e_post, e_reg_pre, e_pre | e_dtor, 2);   /* Two, post then pre */
+    CHAINTEST2("1 pre, 1 both", "{\"pre\":{},\"sloppy\":{}}",                                                    /* Two, pre then both */
+               e_reg_pre, e_pre | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 3);
+    CHAINTEST2("1 both, 1 pre", "{\"sloppy\":{},\"pre\":{}}",                                                    /* Two, both then pre */
+               e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre, e_pre, 3);
+    CHAINTEST2("1 post, 1 both", "{\"post\":{},\"sloppy\":{}}",                                                 /* Two, post then both */
+               e_reg_post, e_post, e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 3);
+    CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}",                                                 /* Two, both then post */
+               e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3);
+
+    /* Plugins dropping updates */
+    drop = 0;
+    CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}",                            /* Two, both chains, drop at filter 0 */
+               e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, 0, -1);
+    drop = 1;
+    CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}",                            /* Two, both chains, drop at filter 1 */
+               e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, e_pre, -1);
+    drop = 2;
+    CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}",                            /* Two, both chains, drop at filter 2 */
+               e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre, -1);
+    drop = 3;
+    CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}",                            /* Two, both chains, drop at filter 3 */
+               e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre | e_post, -1);
+    drop = -1;
+
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/test/dbChannelTest.c'
--- src/ioc/db/test/dbChannelTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbChannelTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,260 @@
+/*************************************************************************\
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+ \*************************************************************************/
+
+/*
+ *  Author: Andrew Johnson <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#include "dbChannel.h"
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "recSup.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+/* Expected call bit definitions */
+#define e_start         0x00000001
+#define e_abort         0x00000002
+#define e_end           0x00000004
+#define e_null          0x00000008
+#define e_boolean       0x00000010
+#define e_integer       0x00000020
+#define e_double        0x00000040
+#define e_string        0x00000080
+#define e_start_map     0x00000100
+#define e_map_key       0x00000200
+#define e_end_map       0x00000400
+#define e_start_array   0x00000800
+#define e_end_array     0x00001000
+#define e_open          0x00002000
+#define e_reg_pre       0x00004000
+#define e_reg_post      0x00008000
+#define e_report        0x00010000
+#define e_close         0x00020000
+
+#define r_any (e_start | e_abort | e_end | \
+        e_null | e_boolean | e_integer | e_double | e_string | \
+        e_start_map | e_map_key | e_end_map | e_start_array | e_end_array)
+#define r_scalar (e_start | e_abort | e_end | \
+        e_null | e_boolean | e_integer | e_double | e_string)
+
+unsigned int e, r;
+#define p_ret(x) return r & x ? parse_continue : parse_stop
+
+parse_result p_start(chFilter *filter)
+{
+    testOk(e & e_start, "parse_start called");
+    p_ret(e_start);
+}
+
+void p_abort(chFilter *filter)
+{
+    testOk(e & e_abort, "parse_abort called");
+}
+
+parse_result p_end(chFilter *filter)
+{
+    testOk(e & e_end, "parse_end called");
+    p_ret(e_end);
+}
+
+parse_result p_null(chFilter *filter)
+{
+    testOk(e & e_null, "parse_null called");
+    p_ret(e_null);
+}
+parse_result p_boolean(chFilter *filter, int boolVal)
+{
+    testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal);
+    p_ret(e_boolean);
+}
+parse_result p_integer(chFilter *filter, long integerVal)
+{
+    testOk(e & e_integer, "parse_integer called, val = %ld", integerVal);
+    p_ret(e_integer);
+}
+parse_result p_double(chFilter *filter, double doubleVal)
+{
+    testOk(e & e_double, "parse_double called, val = %g", doubleVal);
+    p_ret(e_double);
+}
+parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen)
+{
+    testOk(e & e_string, "parse_string called, val = '%.*s'", (int) stringLen,
+            stringVal);
+    p_ret(e_string);
+}
+
+parse_result p_start_map(chFilter *filter)
+{
+    testOk(e & e_start_map, "parse_start_map called");
+    p_ret(e_start_map);
+}
+parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen)
+{
+    testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", (int) stringLen, key);
+    p_ret(e_map_key);
+}
+parse_result p_end_map(chFilter *filter)
+{
+    testOk(e & e_end_map, "parse_end_map called");
+    p_ret(e_end_map);
+}
+
+parse_result p_start_array(chFilter *filter)
+{
+    testOk(e & e_start_array, "parse_start_array called");
+    p_ret(e_start_array);
+}
+parse_result p_end_array(chFilter *filter)
+{
+    testOk(e & e_end_array, "parse_end_array called");
+    p_ret(e_end_array);
+}
+
+long c_open(chFilter *filter)
+{
+    testOk(e & e_open, "channel_open called");
+    return 0;
+}
+void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    testOk(e & e_reg_pre, "channel_register_pre called");
+}
+void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+    testOk(e & e_reg_post, "channel_register_post called");
+}
+void c_report(chFilter *filter, int level, const unsigned short indent)
+{
+    testOk(e & e_report, "channel_report called, level = %d", level);
+}
+void c_close(chFilter *filter)
+{
+    testOk(e & e_close, "channel_close called");
+}
+
+chFilterIf testIf =
+    { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double,
+      p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array,
+      c_open, c_reg_pre, c_reg_post, c_report, c_close };
+
+void xRecord_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(dbChannelTest)
+{
+    dbChannel *pch;
+
+    testPlan(69);
+
+    testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL));
+
+    xRecord_registerRecordDeviceDriver(pdbbase);
+    testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL));
+    testOk(!!pdbbase, "pdbbase was set");
+
+    r = e = 0;
+    /* dbChannelTest() checks record and field names */
+    testOk1(!dbChannelTest("x.NAME"));
+    testOk1(!dbChannelTest("x.VAL"));
+    testOk1(!dbChannelTest("x."));
+    testOk1(!dbChannelTest("x"));
+    testOk(dbChannelTest("y"), "Test, nonexistent record");
+    testOk(dbChannelTest("x.NOFIELD"), "Test, nonexistent field");
+
+    /* dbChannelTest() allows but ignores field modifiers */
+    testOk1(!dbChannelTest("x.NAME$"));
+    testOk1(!dbChannelTest("x.{}"));
+    testOk1(!dbChannelTest("x.VAL{\"json\":true}"));
+
+    /* dbChannelCreate() accepts field modifiers */
+    testOk1(!!(pch = dbChannelCreate("x.{}")));
+    if (pch) dbChannelDelete(pch);
+    testOk1(!!(pch = dbChannelCreate("x.VAL{}")));
+    if (pch) dbChannelDelete(pch);
+    testOk1(!!(pch = dbChannelCreate("x.NAME$")));
+    testOk1(pch && pch->addr.no_elements > 1);
+    if (pch) dbChannelDelete(pch);
+    testOk1(!!(pch = dbChannelCreate("x.NAME${}")));
+    testOk1(pch && pch->addr.no_elements > 1);
+    if (pch) dbChannelDelete(pch);
+
+    /* dbChannelCreate() rejects bad PVs */
+    testOk(!dbChannelCreate("y"), "Create, bad record");
+    testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field");
+    testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON");
+    testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter");
+
+    dbRegisterFilter("any", &testIf, NULL);
+
+    /* Parser event rejection by filter */
+    e = e_start;
+    testOk1(!dbChannelCreate("x.{\"any\":null}"));
+
+    r = e_start;
+    e = e_start | e_null | e_abort;
+    testOk1(!dbChannelCreate("x.{\"any\":null}"));
+
+    r = e_start | e_null;
+    e = e_start | e_null | e_end;
+    testOk1(!dbChannelCreate("x.{\"any\":null}"));
+
+    /* Successful parsing... */
+    r = r_any;
+    e = e_start | e_null | e_end;
+    testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}")));
+    e = e_close;
+    if (pch) dbChannelDelete(pch);
+
+    dbRegisterFilter("scalar", &testIf, NULL);
+
+    e = e_start | e_null | e_end;
+    testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}")));
+
+    e = e_report;
+    dbChannelShow(pch, 1, 2);
+
+    e = e_close;
+    if (pch) dbChannelDelete(pch);
+
+    e = e_start | e_start_array | e_boolean | e_integer | e_end_array
+            | e_end;
+    testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}")));
+    e = e_close;
+    if (pch) dbChannelDelete(pch);
+
+    e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map
+            | e_end;
+    testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")));
+    e = e_close;
+    if (pch) dbChannelDelete(pch);
+
+    /* More event rejection */
+    r = r_scalar;
+    e = e_start | e_start_array | e_abort;
+    testOk1(!dbChannelCreate("x.{\"scalar\":[null]}"));
+
+    e = e_start | e_start_map | e_abort;
+    testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
+
+    dbFreeBase(pdbbase);
+
+    return testDone();
+}
+
+#define GEN_SIZE_OFFSET
+#include "xRecord.h"
+
+#include <recSup.h>
+#include <epicsExport.h>
+
+static rset xRSET;
+epicsExportAddress(rset,xRSET);

=== added file 'src/ioc/db/test/dbChannelTest.db'
--- src/ioc/db/test/dbChannelTest.db	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbChannelTest.db	2012-05-30 18:10:27 +0000
@@ -0,0 +1,2 @@
+record(x, x) {}
+

=== added file 'src/ioc/db/test/dbStateTest.c'
--- src/ioc/db/test/dbStateTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbStateTest.c	2012-05-30 18:10:27 +0000
@@ -0,0 +1,56 @@
+/*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ *  Author: Ralph Lange <[email protected]>
+ */
+
+#include <string.h>
+
+#include "dbState.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+MAIN(dbStateTest)
+{
+    dbStateId red, red2, blue, blue2;
+    int i;
+
+    testPlan(20);
+
+    testOk(!dbStateFind("y"), "Finding nonexisting state fails");
+
+    testOk(!!(red = dbStateCreate("red")), "Create state 'red'");
+    testOk((red2 = dbStateFind("red")) == red, "Find 'red' returns correct id");
+    testOk((red2 = dbStateCreate("red")) == red, "Create existing 'red' returns correct id");
+    testOk(!dbStateFind("y"), "Finding nonexisting state still fails");
+
+    testOk(!!(blue = dbStateCreate("blue")), "Create state 'blue'");
+    testOk((blue2 = dbStateFind("blue")) == blue, "Find 'blue' returns correct id");
+    testOk((blue2 = dbStateCreate("blue")) == blue, "Create existing 'blue' returns correct id");
+    testOk(!dbStateFind("y"), "Finding nonexisting state still fails");
+
+    testOk((i = dbStateGet(red)) == 0, "Default 'red' state is 0");
+    testOk((i = dbStateGet(blue)) == 0, "Default 'blue' state is 0");
+    dbStateSet(red);
+    testOk((i = dbStateGet(red)) == 1, "After setting, 'red' state is 1");
+    testOk((i = dbStateGet(blue)) == 0, "'blue' state is 0");
+    dbStateSet(blue);
+    testOk((i = dbStateGet(blue)) == 1, "After setting, 'blue' state is 1");
+    testOk((i = dbStateGet(red)) == 1, "'red' state is 1");
+    dbStateClear(blue);
+    testOk((i = dbStateGet(blue)) == 0, "After clearing, 'blue' state is 0");
+    testOk((i = dbStateGet(red)) == 1, "'red' state is 1");
+    dbStateClear(red);
+    testOk((i = dbStateGet(red)) == 0, "After clearing, 'red' state is 0");
+    testOk((i = dbStateGet(blue)) == 0, "'red' state is 0");
+
+    testOk(!dbStateFind("y"), "Finding nonexisting state still fails");
+
+    return testDone();
+}

=== added file 'src/ioc/db/test/xRecord.dbd'
--- src/ioc/db/test/xRecord.dbd	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/xRecord.dbd	2012-05-30 18:10:27 +0000
@@ -0,0 +1,12 @@
+# This is a combined minimal DBD and DB file
+
+recordtype(x) {
+  field(NAME, DBF_STRING) {
+    prompt("Record Name")
+    special(SPC_NOMOD)
+    size(61)
+  }
+  field(VAL, DBF_LONG) {
+    prompt("Value")
+  }
+}

=== modified file 'src/ioc/dbStatic/dbBase.h'
--- src/ioc/dbStatic/dbBase.h	2010-10-05 19:27:37 +0000
+++ src/ioc/dbStatic/dbBase.h	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 /* $Revision-Id$
  *
@@ -19,7 +19,7 @@
 #include "dbFldTypes.h"
 #include "ellLib.h"
 #include "dbDefs.h"
-
+
 typedef struct dbMenu {
 	ELLNODE		node;
 	char		*name;
@@ -67,7 +67,7 @@
 	long		number;		/*number of brkInt in this table*/
 	struct brkInt	*paBrkInt;	/* ptr to array of brkInts */
 }brkTable;
-
+
 typedef struct dbFldDes{  /* field description */
 	char	*prompt; 	/*Prompt string for DCT*/
 	char	*name;		/*Field name*/
@@ -128,7 +128,7 @@
 	ELLNODE		node;
 	char		*name;
         char            *type;
-        
+
 }dbVariableDef;
 
 typedef struct dbRecordType {
@@ -164,6 +164,7 @@
 	ELLLIST		functionList;
 	ELLLIST		variableList;
 	ELLLIST		bptList;
+	ELLLIST         filterList;
 	void		*pathPvt;
 	struct dbPvd	*ppvd;
 	struct gphPvt	*pgpHash;

=== modified file 'src/ioc/dbStatic/dbPvdLib.c'
--- src/ioc/dbStatic/dbPvdLib.c	2009-04-09 17:28:59 +0000
+++ src/ioc/dbStatic/dbPvdLib.c	2012-05-30 18:10:27 +0000
@@ -181,6 +181,7 @@
             ellDelete(&pbucket->list, (ELLNODE *)ppvdNode);
             free(ppvdNode);
         }
+        epicsMutexUnlock(pbucket->lock);
         epicsMutexDestroy(pbucket->lock);
         free(pbucket);
     }

=== modified file 'src/ioc/dbStatic/dbStaticLib.c'
--- src/ioc/dbStatic/dbStaticLib.c	2012-05-29 21:44:49 +0000
+++ src/ioc/dbStatic/dbStaticLib.c	2012-05-30 18:10:27 +0000
@@ -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.
 \*************************************************************************/
 /* $Revision-Id$ */
 
@@ -41,7 +41,7 @@
 #include "guigroup.h"
 #include "dbStaticLib.h"
 #include "dbStaticPvt.h"
-
+
 int dbStaticDebug = 0;
 static char *pNullString = "";
 #define messagesize	100
@@ -191,7 +191,7 @@
 sizeof(promptBBGPIB_IO)/sizeof(char *),
 sizeof(promptRF_IO)/sizeof(char *),
 sizeof(promptVXI_IO)/sizeof(char *)};
-
+
 /*forward references for private routines*/
 static FILE *openOutstream(const char *filename);
 static void finishOutstream(FILE *stream);
@@ -203,7 +203,7 @@
 static char *getpMessage(DBENTRY *pdbentry);
 static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname);
 static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length);
-
+
 /* internal routines*/
 static FILE *openOutstream(const char *filename)
 {
@@ -347,7 +347,7 @@
     if(plink->text) free(plink->text);
     memset((char *)plink,0,sizeof(struct link));
 }
-
+
 void dbFreePath(DBBASE *pdbbase)
 {
     ELLLIST	*ppathList;
@@ -366,7 +366,7 @@
     return;
 }
 
-
+
 static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind)
 {
     switch(plink->type) {
@@ -407,7 +407,7 @@
     }
     return(S_dbLib_badLink);
 }
-
+
 static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
 {
     char		message[200];
@@ -439,7 +439,7 @@
     strcat(pmessage,mess);
     errMessage(status,pmessage);
 }
-
+
 static void zeroDbentry(DBENTRY *pdbentry)
 {
     /*NOTE that pdbbase, message, and formpvt MUST NOT be set to NULL*/
@@ -466,7 +466,7 @@
     dbFldDes	*pflddes;
     DBLINK	*plink;
     char	*pname;
-    
+
     dbGetFieldAddress(pdbentry);
     pflddes = pdbentry->pflddes;
     if(!pflddes) return(-1);
@@ -489,7 +489,7 @@
     }
     return(S_dbLib_badLink);
 }
-
+
 /*Public only for dbStaticNoRun*/
 dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry)
 {
@@ -526,7 +526,7 @@
     pflddes->ftPvt = pdbDeviceMenu;
     return(pdbDeviceMenu);
 }
-
+
 /* Beginning of Public Routines */
 
 #define INC_SIZE	256
@@ -555,7 +555,7 @@
     strcat(*string,src);
     *stringLength += strlen(src);
 }
-
+
 dbBase * epicsShareAPI dbAllocBase(void)
 {
     dbBase	*pdbbase;
@@ -568,6 +568,7 @@
     ellInit(&pdbbase->functionList);
     ellInit(&pdbbase->variableList);
     ellInit(&pdbbase->bptList);
+    ellInit(&pdbbase->filterList);
     gphInitPvt(&pdbbase->pgpHash,256);
     dbPvdInitPvt(pdbbase);
     return (pdbbase);
@@ -595,7 +596,7 @@
     brkTable		*pbrkTableNext;
     int			i;
     DBENTRY		dbentry;
-    
+
 
     dbInitEntry(pdbbase,&dbentry);
     pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
@@ -620,7 +621,7 @@
 	    free((void *)pdbFldDes->initial);
 	    if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) {
 		dbDeviceMenu *pdbDeviceMenu;
-		
+
 		pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt;
 		free((void *)pdbDeviceMenu->papChoice);
 		free((void *)pdbDeviceMenu);
@@ -729,12 +730,12 @@
     free((void *)pdbbase);
     return;
 }
-
+
 DBENTRY * epicsShareAPI dbAllocEntry(dbBase *pdbbase)
 {
     DBENTRY *pdbentry;
 
-    pdbentry = dbmfMalloc(sizeof(DBENTRY)); 
+    pdbentry = dbmfMalloc(sizeof(DBENTRY));
     memset(pdbentry,'\0',sizeof(DBENTRY));
     pdbentry->pdbbase = pdbbase;
     return(pdbentry);
@@ -780,7 +781,7 @@
     pto->formpvt = NULL;
 }
 
-
+
 long epicsShareAPI dbPath(DBBASE *pdbbase,const char *path)
 {
     if(!pdbbase) return(-1);
@@ -796,7 +797,7 @@
     const char	*plast;
     unsigned	expectingPath;
     unsigned	sawMissingPath;
-	
+
     if(!pdbbase) return(-1);
     ppathList = (ELLLIST *)pdbbase->pathPvt;
     if(!ppathList) {
@@ -865,7 +866,7 @@
 {
     ELLLIST	*ppathList;
     dbPathNode *pdbPathNode;
-	
+
     if(!pdbbase) return(-1);
     ppathList = (ELLLIST *)pdbbase->pathPvt;
 
@@ -877,7 +878,7 @@
     return 0;
 }
 
-
+
 long epicsShareAPI dbWriteRecord(DBBASE *ppdbbase,const char *filename,
 	const char *precordTypename,int level)
 {
@@ -974,7 +975,7 @@
     dbFinishEntry(pdbentry);
     return(0);
 }
-
+
 long epicsShareAPI dbWriteMenu(
     DBBASE *ppdbbase,const char *filename,const char *menuName)
 {
@@ -1017,7 +1018,7 @@
     }
     return(0);
 }
-
+
 long epicsShareAPI dbWriteRecordType(
     DBBASE *pdbbase,const char *filename,const char *recordTypeName)
 {
@@ -1114,7 +1115,7 @@
     }
     return(0);
 }
-
+
 long epicsShareAPI dbWriteDevice(DBBASE *pdbbase,const char *filename)
 {
     FILE *stream;
@@ -1156,7 +1157,7 @@
     }
     return(0);
 }
-
+
 long epicsShareAPI dbWriteDriver(DBBASE *pdbbase,const char *filename)
 {
     FILE *stream;
@@ -1227,7 +1228,7 @@
     }
     return(0);
 }
-
+
 long epicsShareAPI dbWriteBreaktable(DBBASE *pdbbase,const char *filename)
 {
     FILE *stream;
@@ -1262,7 +1263,7 @@
     }
     return(0);
 }
-
+
 long epicsShareAPI dbFindRecordType(DBENTRY *pdbentry,const char *recordType)
 {
     dbBase	*pdbbase = pdbentry->pdbbase;
@@ -1306,7 +1307,7 @@
 {
     return(ellCount(&pdbentry->pdbbase->recordTypeList));
 }
-
+
 long epicsShareAPI dbPutRecordAttribute(
     DBENTRY *pdbentry, const char *name, const char*value)
 {
@@ -1382,7 +1383,7 @@
 {
     return dbGetAttributePart(pdbentry, &pname);
 }
-
+
 long epicsShareAPI dbFirstField(DBENTRY *pdbentry,int dctonly)
 {
 
@@ -1426,7 +1427,7 @@
 	indfield++;
     }
 }
-
+
 int epicsShareAPI dbGetFieldType(DBENTRY *pdbentry)
 {
     dbFldDes  	*pflddes = pdbentry->pflddes;
@@ -1450,7 +1451,7 @@
     n = 0;
     for(indfield=0; indfield<precordType->no_fields; indfield++) {
 	pflddes = precordType->papFldDes[indfield];
-	if(dctonly && (pflddes->field_type==DBF_DEVICE) 
+	if(dctonly && (pflddes->field_type==DBF_DEVICE)
 	&& (ellCount(&precordType->devList)==0) ) continue;
 	if(!dctonly || pflddes->promptgroup) n++;
     }
@@ -1488,7 +1489,7 @@
     if(!pflddes) return(0);
     return(pflddes->promptgroup);
 }
-
+
 long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
 {
     dbRecordType	*precordType = pdbentry->precordType;
@@ -1535,7 +1536,7 @@
     if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
     return(0);
 }
-
+
 long epicsShareAPI dbDeleteAliases(DBENTRY *pdbentry)
 {
     dbBase		*pdbbase = pdbentry->pdbbase;
@@ -1653,7 +1654,7 @@
         return dbFindField(pdbentry, ++pname);
     return 0;
 }
-
+
 long epicsShareAPI dbFirstRecord(DBENTRY *pdbentry)
 {
     dbRecordType	*precordType = pdbentry->precordType;
@@ -1706,7 +1707,7 @@
     if(!precnode) return NULL;
     return precnode->recordname;
 }
-
+
 long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName)
 {
     dbBase		*pdbbase = pdbentry->pdbbase;
@@ -1752,7 +1753,7 @@
     /*Leave pdbentry pointing to newly renamed record*/
     return(dbFindRecord(pdbentry,newName));
 }
-
+
 long epicsShareAPI dbVisibleRecord(DBENTRY *pdbentry)
 {
     dbRecordNode	*precnode = pdbentry->precnode;
@@ -1778,7 +1779,7 @@
     if(!precnode) return 0;
     return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0;
 }
-
+
 long epicsShareAPI dbCreateAlias(DBENTRY *pdbentry, const char *alias)
 {
     dbRecordType	*precordType = pdbentry->precordType;
@@ -1828,7 +1829,7 @@
     if(!precnode) return 0;
     return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0;
 }
-
+
 long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK)
 {
     dbRecordType	*precordType = pdbentry->precordType;
@@ -1882,7 +1883,7 @@
     /*Leave pdbentry pointing to newRecordName*/
     return(dbFindRecord(pdbentry,newRecordName));
 }
-
+
 long epicsShareAPI dbFindFieldPart(DBENTRY *pdbentry,const char **ppname)
 {
     dbRecordType *precordType = pdbentry->precordType;
@@ -1967,7 +1968,7 @@
 
 int epicsShareAPI dbFoundField(DBENTRY *pdbentry)
 { return((pdbentry->pfield) ? TRUE : FALSE); }
-
+
 char * epicsShareAPI dbGetString(DBENTRY *pdbentry)
 {
     dbFldDes  	*pflddes = pdbentry->pflddes;
@@ -1997,7 +1998,7 @@
     case DBF_DEVICE:
 	return(dbGetStringNum(pdbentry));
     case DBF_INLINK:
-    case DBF_OUTLINK: 
+    case DBF_OUTLINK:
 	if(!pfield) {strcpy(message,"Field not found"); return(message);}
 	plink = (DBLINK *)pfield;
 	switch(plink->type) {
@@ -2138,7 +2139,7 @@
     }
     return (message);
 }
-
+
 static void cvtDecimalOrHexToShort(char *from,short *value)
 {
     if(strspn(from,"0x")==2 || strspn(from,"0X")==2) {
@@ -2264,7 +2265,7 @@
 		goto done;
 	    }
 	    switch (plink->type) {
-	    case CONSTANT: 
+	    case CONSTANT:
 	    case PV_LINK: {
 	    	    short	ppOpt = 0;
 		    short	msOpt = 0;
@@ -2428,7 +2429,7 @@
 		}
                 break;
             case GPIB_IO: {
-                    char *end; 
+                    char *end;
 
 		    if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
 		    pstr = end + 1;
@@ -2545,7 +2546,7 @@
     }
     return(status);
 }
-
+
 char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring)
 {
     dbFldDes  	*pflddes = pdbentry->pflddes;
@@ -2591,13 +2592,13 @@
 		return(message);
 	    }
 	    switch (pflddes->field_type) {
-	    case DBF_CHAR : 
+	    case DBF_CHAR :
 		if(value<-128 || value>127) {
 		    strcpy(message,"must have -128<=value<=127");
 		    return(message);
 		}
 		return(NULL);
-	    case DBF_SHORT : 
+	    case DBF_SHORT :
 		if(value<-32768 || value>32767) {
 		    strcpy(message,"must have -32768<=value<=32767");
 		    return(message);
@@ -2626,14 +2627,14 @@
 		return(message);
 	    }
 	    switch (pflddes->field_type) {
-	    case DBF_UCHAR : 
+	    case DBF_UCHAR :
 		if(value>255) {
 		    strcpy(message,"must have 0<=value<=255");
 		    return(message);
 		}
 		return(NULL);
 	    case DBF_ENUM:
-	    case DBF_USHORT : 
+	    case DBF_USHORT :
 		if(value>65535) {
 		    strcpy(message,"must have 0<=value<=65535");
 		    return(message);
@@ -2646,7 +2647,7 @@
 	    }
 	}
     case DBF_FLOAT:
-    case DBF_DOUBLE: {	
+    case DBF_DOUBLE: {
 	    double value;
 	    char  *endp;
 
@@ -2661,7 +2662,7 @@
 	    dbMenu	*pdbMenu = (dbMenu *)pflddes->ftPvt;
 	    char	*pchoice;
 	    int		i;
-	    
+
 	    if(!pdbMenu) return(NULL);
 	    for (i = 0; i < pdbMenu->nChoice; i++) {
 		if(!(pchoice = pdbMenu->papChoiceValue[i])) continue;
@@ -2699,7 +2700,7 @@
     strcpy(message,"Not a valid field type");
     return (message);
 }
-
+
 char *epicsShareAPI dbGetRange(DBENTRY *pdbentry)
 {
     dbFldDes  	*pflddes = pdbentry->pflddes;
@@ -2729,14 +2730,14 @@
     strcpy(message,"Not a valid field type");
     return (message);
 }
-
+
 long epicsShareAPI dbFirstInfo(DBENTRY *pdbentry)
 {
     dbRecordNode *precnode = pdbentry->precnode;
-    
+
     pdbentry->pinfonode = NULL;
     if (!precnode) return (S_dbLib_recNotFound);
-    
+
     pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList);
     return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound);
 }
@@ -2745,11 +2746,11 @@
 {
     dbRecordNode *precnode = pdbentry->precnode;
     dbInfoNode *pinfo;
-    
+
     if (!precnode) return (S_dbLib_recNotFound);
     pinfo = pdbentry->pinfonode;
     if (!pinfo) return (S_dbLib_infoNotFound);
-    
+
     pinfo = (dbInfoNode *)ellNext(&pinfo->node);
     pdbentry->pinfonode = pinfo;
     return (pinfo ? 0 : S_dbLib_infoNotFound);
@@ -2759,10 +2760,10 @@
 {
     dbRecordNode *precnode = pdbentry->precnode;
     dbInfoNode *pinfo;
-    
+
     pdbentry->pinfonode = NULL;
     if (!precnode) return(S_dbLib_recNotFound);
-    
+
     pinfo = (dbInfoNode *)ellFirst(&precnode->infoList);
     while (pinfo) {
 	if (!strcmp(pinfo->name, name)) {
@@ -2778,7 +2779,7 @@
 {
     dbRecordNode	*precnode = pdbentry->precnode;
     dbInfoNode		*pinfo = pdbentry->pinfonode;
-    
+
     if (!precnode) return (S_dbLib_recNotFound);
     if (!pinfo) return (S_dbLib_infoNotFound);
     ellDelete(&precnode->infoList,&pinfo->node);
@@ -2841,11 +2842,11 @@
     dbInfoNode *pinfo;
     dbRecordNode *precnode = pdbentry->precnode;
     if (!precnode) return (S_dbLib_recNotFound);
-    
+
     dbFindInfo(pdbentry, name);
     pinfo = pdbentry->pinfonode;
     if (pinfo) return (dbPutInfoString(pdbentry, string));
-    
+
     /*Create new info node*/
     pinfo = calloc(1,sizeof(dbInfoNode));
     if (!pinfo) return (S_dbLib_outMem);
@@ -2875,7 +2876,7 @@
     if(!pgph) return(NULL);
     return((brkTable *)pgph->userPvt);
 }
-
+
 dbMenu * epicsShareAPI dbFindMenu(dbBase *pdbbase,const char *name)
 {
     GPHENTRY *pgph;
@@ -2933,7 +2934,7 @@
     }
     return (-1);
 }
-
+
 char * epicsShareAPI dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index)
 {
     dbFldDes  	*pflddes = pdbentry->pflddes;
@@ -2996,13 +2997,13 @@
     }
     return (-1);
 }
-
+
 drvSup * epicsShareAPI dbFindDriver(dbBase *pdbbase, const char *name) {
     GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
     if (!pgph) return NULL;
     return (drvSup *) pgph->userPvt;
 }
-
+
 int epicsShareAPI dbAllocForm(DBENTRY *psave)
 {
     DBENTRY	dbEntry;
@@ -3076,7 +3077,7 @@
     dbFinishEntry(pdbentry);
     return(nlines);
 }
-
+
 long epicsShareAPI dbFreeForm(DBENTRY *pdbentry)
 {
     if(pdbentry->formpvt) {
@@ -3093,7 +3094,7 @@
     if(!pform) return(NULL);
     return(pform->prompt);
 }
-
+
 char  ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry)
 {
     struct form *pform = pdbentry->formpvt;
@@ -3105,7 +3106,7 @@
     if(!plink) return(NULL);
     value = pform->value;
     switch(pform->linkType) {
-    case FORM_CONSTANT: 
+    case FORM_CONSTANT:
 	if(plink->value.constantStr) {
 	    strcpy(*value,plink->value.constantStr);
 	} else {
@@ -3263,7 +3264,7 @@
     }
     return(pform->value);
 }
-
+
 long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value)
 {
     struct form *pform = pdbentry->formpvt;
@@ -3279,7 +3280,7 @@
     if(!plink) return(S_dbLib_badLink);
     verify = pform->verify;
     switch(pform->linkType) {
-    case FORM_CONSTANT: 
+    case FORM_CONSTANT:
 	**verify = 0; /*Initialize to no error*/
 	if(**value == '\0') break;
 	dvalue = epicsStrtod(*value,&endp);
@@ -3580,7 +3581,7 @@
 	**verify = 0;
 	break;
     case FORM_VXI_IO:
-	plink->value.vxiio.flag = 
+	plink->value.vxiio.flag =
 	    ((strchr(*value,'Y')||strchr(*value,'y') ? VXIDYNAMIC : VXISTATIC));
 	value++; verify++;
 	lvalue = strtol(*value,&endp,0);
@@ -3619,7 +3620,7 @@
     }
     return(status);
 }
-
+
 char  ** epicsShareAPI dbVerifyForm(DBENTRY *pdbentry,char **value)
 {
     struct form *pform = pdbentry->formpvt;
@@ -3664,7 +3665,7 @@
     dbFinishEntry(pdbentry);
     return(rtnval);
 }
-
+
 int  epicsShareAPI dbGetNLinks(DBENTRY *pdbentry)
 {
     dbRecordType	*precordType = pdbentry->precordType;
@@ -3715,7 +3716,7 @@
     }
     return(-1);
 }
-
+
 long  epicsShareAPI dbCvtLinkToConstant(DBENTRY *pdbentry)
 {
     dbFldDes	*pflddes;
@@ -3779,7 +3780,7 @@
     }
     return(S_dbLib_badLink);
 }
-
+
 void  epicsShareAPI dbDumpPath(DBBASE *pdbbase)
 {
     ELLLIST	*ppathList;
@@ -3821,7 +3822,7 @@
     }
     dbWriteMenuFP(pdbbase,stdout,menuName);
 }
-
+
 void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName)
 {
     dbRecordType *pdbRecordType;
@@ -3863,7 +3864,7 @@
 	if(recordTypeName) break;
     }
 }
-
+
 void  epicsShareAPI dbDumpField(
     DBBASE *pdbbase,const char *recordTypeName,const char *fname)
 {
@@ -3955,7 +3956,7 @@
 	if(recordTypeName) break;
     }
 }
-
+
 void  epicsShareAPI dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName)
 {
     dbRecordType *pdbRecordType;
@@ -4051,7 +4052,7 @@
     }
     return;
 }
-
+
 static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
 	"GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
 void  epicsShareAPI dbReportDeviceConfig(dbBase *pdbbase,FILE *report)

=== modified file 'src/ioc/dbStatic/link.h'
--- src/ioc/dbStatic/link.h	2010-10-05 19:27:37 +0000
+++ src/ioc/dbStatic/link.h	2012-05-30 18:10:27 +0000
@@ -1,27 +1,24 @@
 /*************************************************************************\
-* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
-* EPICS BASE Versions 3.13.7
-* and higher are distributed subject to a Software License Agreement found
+* EPICS BASE is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution. 
 \*************************************************************************/
 /* link.h */
-/* base/include $Revision-Id$ */
+/* $Id$ */
 
 /*
- *      Original Author: Bob Dalesio
- *      Current Author:  Marty Kraimer
- *      Date:            6-1-90
+ * Original Authors: Bob Dalesio, Marty Kraimer
  */
 
+#ifndef INC_link_H
+#define INC_link_H
+
 #include "dbDefs.h"
 #include "shareLib.h"
 
-#ifndef INClinkh
-#define INClinkh 1
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -55,8 +52,8 @@
 /* structure of a PV_LINK DB_LINK and a CA_LINK	*/
 /*Options defined by pvlMask			*/
 #define pvlOptMsMode       0x3	/*Maximize Severity mode selection*/
-#define pvlOptNMS            0	/*Dont Maximize Severity*/
-#define pvlOptMS             1	/*Maximize Severity allways*/
+#define pvlOptNMS            0	/*Don't Maximize Severity*/
+#define pvlOptMS             1	/*Maximize Severity always*/
 #define pvlOptMSI            2	/*Maximize Severity if INVALID*/
 #define pvlOptMSS            3	/*Maximize Severity and copy Status*/
 #define pvlOptPP           0x4	/*Process Passive*/
@@ -77,14 +74,15 @@
 };
 
 struct dbCommon;
+struct pvlet;
 
 struct pv_link {
-	char	*pvname;	/*pvname to link to*/
-	struct dbCommon *precord;	/*Address of record containing link*/
-	void	*pvt;		/*CA or DB private*/
-	LINKCVT	getCvt;		/*input conversion function*/
-	short	pvlMask;	/*Options mask*/
-	short	lastGetdbrType;	/*last dbrType for DB or CA get*/
+	char	*pvname;		/* pvname link points to */
+	struct dbCommon *precord;	/* Address of record owning link */
+	void	*pvt;			/* CA or DB private */
+	LINKCVT	getCvt;			/* input conversion function */
+	short	pvlMask;		/* Options mask */
+	short	lastGetdbrType;		/* last dbrType for DB or CA get */
 };
 
 /* structure of a VME io channel */
@@ -150,13 +148,12 @@
 
 /* structure of an instrument io link */
 struct	instio {
-	char	*string;		/* the cat of location.
-							signal.parameter  */
+	char	*string;
 };
 
 /* structure of a vxi link */
 struct	vxiio{
-	short	flag;				/* 0 = frame/slot, 1 = SA */
+	short	flag;			/* 0 = frame/slot, 1 = SA */
 	short	frame;
 	short	slot;
 	short	la;				/* logical address if flag =1 */
@@ -165,7 +162,7 @@
 };
 
 /* union of possible address structures */
-union value{
+union value {
 	char		*constantStr;	/*constant string*/
 	struct macro_link macro_link;	/* link containing macro substitution*/
 	struct pv_link	pv_link;	/* link to process variable*/
@@ -180,9 +177,9 @@
 	struct	vxiio	vxiio;		/* vxi io */
 };
 
-struct link{
-	union value	value;
-	short		type;
+struct link {
+	union value value;
+	short type;
         char		*text;		/* original INP/OUT link text */
 };
 
@@ -191,4 +188,4 @@
 #ifdef __cplusplus
 }
 #endif
-#endif /*INClinkh*/
+#endif /* INC_link_H */

=== modified file 'src/ioc/misc/iocInit.c'
--- src/ioc/misc/iocInit.c	2011-09-26 21:42:15 +0000
+++ src/ioc/misc/iocInit.c	2012-05-30 18:10:27 +0000
@@ -41,7 +41,6 @@
 #include "taskwd.h"
 #include "callback.h"
 #include "dbCommon.h"
-#include "dbLock.h"
 #include "devSup.h"
 #include "drvSup.h"
 #include "menuConvert.h"
@@ -59,6 +58,7 @@
 #include "initHooks.h"
 #include "epicsExit.h"
 #include "epicsSignal.h"
+#include "dbChannel.h"
 
 #define epicsExportSharedSymbols
 #include "epicsRelease.h"
@@ -426,48 +426,18 @@
 static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord,
     void *user)
 {
+    dbFldDes **papFldDes = pdbRecordType->papFldDes;
+    short *link_ind = pdbRecordType->link_ind;
     devSup *pdevSup;
     int j;
 
-    /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */
     /* For all the links in the record type... */
     for (j = 0; j < pdbRecordType->no_links; j++) {
-        dbFldDes *pdbFldDes =
-            pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
+        dbFldDes *pdbFldDes = papFldDes[link_ind[j]];
         DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
 
-        if (plink->type == PV_LINK) {
-            DBADDR dbaddr;
-
-            if (plink == &precord->tsel) recGblTSELwasModified(plink);
-            if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP))
-                && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) {
-                DBADDR  *pdbAddr;
-
-                plink->type = DB_LINK;
-                pdbAddr = dbCalloc(1,sizeof(struct dbAddr));
-                *pdbAddr = dbaddr; /*structure copy*/;
-                plink->value.pv_link.pvt = pdbAddr;
-            } else {/*It is a CA link*/
-
-                if (pdbFldDes->field_type == DBF_INLINK) {
-                    plink->value.pv_link.pvlMask |= pvlOptInpNative;
-                }
-                dbCaAddLink(plink);
-                if (pdbFldDes->field_type == DBF_FWDLINK) {
-                    char *pperiod =
-                        strrchr(plink->value.pv_link.pvname,'.');
-
-                    if (pperiod && strstr(pperiod,"PROC")) {
-                        plink->value.pv_link.pvlMask |= pvlOptFWD;
-                    } else {
-                        errlogPrintf("%s.FLNK is a Channel Access Link "
-                            " but does not link to a PROC field\n",
-                                precord->name);
-                    }
-                }
-            }
-        }
+        if (plink->type == PV_LINK)
+            dbInitLink(precord, plink, pdbFldDes->field_type);
     }
     pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp);
     if (pdevSup) {
@@ -491,6 +461,7 @@
 
 static void initDatabase(void)
 {
+    dbChannelInit();
     iterateRecords(doInitRecord0, NULL);
     iterateRecords(doResolveLinks, NULL);
     iterateRecords(doInitRecord1, NULL);

=== modified file 'src/ioc/registry/registerRecordDeviceDriver.pl'
--- src/ioc/registry/registerRecordDeviceDriver.pl	2012-03-27 15:47:59 +0000
+++ src/ioc/registry/registerRecordDeviceDriver.pl	2012-05-30 18:10:27 +0000
@@ -18,12 +18,18 @@
 use DBD::Parser;
 use EPICS::Readfile;
 use EPICS::Path;
+use EPICS::Getopts;
 use Text::Wrap;
 
+getopts('I@') or
+    die "Usage: registerRecordDeviceDriver [-I dir] in.dbd subroutinename [TOP]";
+
+my @path = map { split /[:;]/ } @EPICS::Getopts::opt_I; # FIXME: Broken on Win32?
+
 my ($file, $subname, $bldTop) = @ARGV;
 
 my $dbd = DBD->new();
-&ParseDBD($dbd, &Readfile($file));
+&ParseDBD($dbd, &Readfile($file, "", \@path));
 
 $Text::Wrap::columns = 75;
 

=== modified file 'src/ioc/rsrv/camessage.c'
--- src/ioc/rsrv/camessage.c	2010-11-01 21:01:04 +0000
+++ src/ioc/rsrv/camessage.c	2012-05-30 18:10:27 +0000
@@ -1,15 +1,19 @@
 /*************************************************************************\
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+*     fuer Materialien und Energie GmbH.
 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
 *     National Laboratory.
 * Copyright (c) 2002 The Regents of the University of California, as
 *     Operator of Los Alamos National Laboratory.
 * EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution. 
+* in file LICENSE that is included with this distribution.
 \*************************************************************************/
 
 /*
- *  Author: Jeffrey O. Hill
+ *  Author: Jeffrey O. Hill <[email protected]>
  *
+ *          Ralph Lange <[email protected]>
  */
 
 #include <stddef.h>
@@ -45,7 +49,7 @@
 #include "rsrv.h"
 #include "server.h"
 
-#define RECORD_NAME(PADDR) ((PADDR)->precord->name)
+#define RECORD_NAME(CHAN) (dbChannelRecord(CHAN)->name)
 
 static EVENTFUNC read_reply;
 
@@ -60,9 +64,9 @@
     putNotify       dbPutNotify;
     caHdrLargeArray msg;
     /*
-     * Include a union of all scalar types 
+     * Include a union of all scalar types
      * including fixed length strings so
-     * that in many cases we can avoid 
+     * that in many cases we can avoid
      * allocating another buffer and only
      * use an rsrv_put_notify from its
      * free list.
@@ -173,8 +177,8 @@
     /*
      * allocate plenty of space for a sprintf() buffer
      */
-    localStatus = cas_copy_in_header ( client, 
-        CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, 
+    localStatus = cas_copy_in_header ( client,
+        CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status,
         ( void * ) &pReqOut );
     if ( localStatus != ECA_NORMAL ) {
         errlogPrintf ( "caserver: Unable to deliver err msg \"%s\" to client because \"%s\"\n",
@@ -187,7 +191,7 @@
      * 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( client->minor_version_number ) ) {
         ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 );
         pReqOut->m_cmmd = htons ( curp->m_cmmd );
@@ -222,7 +226,7 @@
             size += (ca_uint32_t) (diagLen + 1u);
         }
         else {
-            errlogPrintf ( 
+            errlogPrintf (
                 "caserver: vsend_err: epicsVsnprintf detected "
                 "error message truncation, pFormat = \"%s\"\n",
                 pformat );
@@ -282,9 +286,9 @@
 "CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n",
         hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize);
 
-    epicsPrintf (   
-"CAS: Request from %s =>  available=0x%x \tN=%u paddr=%p\n",
-        hostName, mp->m_available, mnum, (pciu?(void *)&pciu->addr:NULL));
+    epicsPrintf (
+"CAS: Request from %s =>  available=0x%x \tN=%u dbch=%p\n",
+        hostName, mp->m_available, mnum, (pciu?(void *)&pciu->dbch:NULL));
 
     if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) {
         epicsPrintf (
@@ -297,7 +301,7 @@
  * logBadIdWithFileAndLineno()
  */
 static void logBadIdWithFileAndLineno(
-struct client       *client, 
+struct client       *client,
 caHdrLargeArray     *mp,
 const void          *pPayload,
 char                *pFileName,
@@ -314,10 +318,10 @@
 /*
  * bad_udp_cmd_action()
  */
-static int bad_udp_cmd_action ( caHdrLargeArray *mp, 
+static int bad_udp_cmd_action ( caHdrLargeArray *mp,
                        void *pPayload, struct client *pClient )
 {
-    log_header ("invalid (damaged?) request code from UDP", 
+    log_header ("invalid (damaged?) request code from UDP",
         pClient, mp, pPayload, 0);
     return RSRV_ERROR;
 }
@@ -325,13 +329,13 @@
 /*
  * udp_echo_action()
  */
-static int udp_echo_action ( caHdrLargeArray *mp, 
+static int udp_echo_action ( caHdrLargeArray *mp,
                            void *pPayload, struct client *pClient )
 {
     char *pPayloadOut;
     int status;
     SEND_LOCK ( pClient );
-    status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, 
+    status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize,
         mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available,
         ( void * ) &pPayloadOut );
     if ( status == ECA_NORMAL ) {
@@ -345,13 +349,13 @@
 /*
  * bad_tcp_cmd_action()
  */
-static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, 
+static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload,
                            struct client *client )
 {
     const char *pCtx = "invalid (damaged?) request code from TCP";
     log_header ( pCtx, client, mp, pPayload, 0 );
 
-    /* 
+    /*
      *  by default, clients dont recover
      *  from this
      */
@@ -365,7 +369,7 @@
 /*
  * tcp_version_action()
  */
-static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, 
+static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload,
                            struct client *client )
 {
     double tmp;
@@ -406,13 +410,13 @@
 /*
  * tcp_echo_action()
  */
-static int tcp_echo_action ( caHdrLargeArray *mp, 
+static int tcp_echo_action ( caHdrLargeArray *mp,
                        void *pPayload, struct client *pClient )
 {
     char *pPayloadOut;
     int status;
     SEND_LOCK ( pClient );
-    status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, 
+    status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize,
         mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available,
         ( void * ) &pPayloadOut );
     if ( status == ECA_NORMAL ) {
@@ -426,7 +430,7 @@
 /*
  * events_on_action ()
  */
-static int events_on_action ( caHdrLargeArray *mp, 
+static int events_on_action ( caHdrLargeArray *mp,
                        void *pPayload, struct client *pClient )
 {
     db_event_flow_ctrl_mode_off ( pClient->evuser );
@@ -436,7 +440,7 @@
 /*
  * events_off_action ()
  */
-static int events_off_action ( caHdrLargeArray *mp, 
+static int events_off_action ( caHdrLargeArray *mp,
                        void *pPayload, struct client *pClient )
 {
     db_event_flow_ctrl_mode_on ( pClient->evuser );
@@ -462,8 +466,8 @@
      * on failure to pre v41 clients
      */
     if ( ! CA_V41 ( pClient->minor_version_number ) ) {
-        send_err ( &pevext->msg, ECA_GETFAIL, pClient, 
-            RECORD_NAME ( &pevext->pciu->addr ) );
+        send_err ( &pevext->msg, ECA_GETFAIL, pClient,
+            RECORD_NAME ( pevext->pciu->dbch ) );
         return;
     }
 
@@ -473,30 +477,30 @@
      * event/put/get callback.
      *
      * Fetched value is zerod in case they
-     * use it even when the status indicates 
+     * use it even when the status indicates
      * failure.
      *
      * The m_cid field in the protocol
      * header is abused to carry the status
      */
-    status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, 
-        pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, 
+    status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size,
+        pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS,
         pevext->msg.m_available, ( void * ) &pPayloadOut );
     if ( status == ECA_NORMAL ) {
         memset ( pPayloadOut, 0, pevext->size );
         cas_commit_msg ( pClient, pevext->size );
     }
     else {
-        send_err ( &pevext->msg, status, pClient, 
+        send_err ( &pevext->msg, status, pClient,
             "server unable to load read access denied response into protocol buffer PV=\"%s max bytes=%u\"",
-            RECORD_NAME ( &pevext->pciu->addr ), rsrvSizeofLargeBufTCP );
+            RECORD_NAME ( pevext->pciu->dbch ), rsrvSizeofLargeBufTCP );
     }
 }
 
 /*
  *  read_reply()
  */
-static void read_reply ( void *pArg, struct dbAddr *paddr, 
+static void read_reply ( void *pArg, struct dbChannel *dbch,
                        int eventsRemaining, db_field_log *pfl )
 {
     ca_uint32_t cid;
@@ -508,12 +512,14 @@
     int status;
     int v41;
     int autosize;
+    int local_fl = 0;
     long item_count;
     ca_uint32_t payload_size;
+    dbAddr *paddr=&dbch->addr;
 
     SEND_LOCK ( pClient );
 
-    /* 
+    /*
      * New clients recv the status of the
      * operation directly to the
      * event/put/get callback.
@@ -543,10 +549,10 @@
         pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available,
         &pPayload );
     if ( status != ECA_NORMAL ) {
-        send_err ( &pevext->msg, status, pClient, 
+        send_err ( &pevext->msg, status, pClient,
             "server unable to load read (or subscription update) response "
             "into protocol buffer PV=\"%s\" max bytes=%u",
-            RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP );
+            RECORD_NAME ( dbch ), rsrvSizeofLargeBufTCP );
         if ( ! eventsRemaining )
             cas_send_bs_msg ( pClient, FALSE );
         SEND_UNLOCK ( pClient );
@@ -564,19 +570,32 @@
         return;
     }
 
-    status = db_get_field_and_count(
-        paddr, pevext->msg.m_dataType, pPayload, &item_count, pfl);
+    /* If filters are involved in a read, create field log and run filters */
+    if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) {
+        pfl = db_create_read_log(dbch);
+        if (pfl) {
+            local_fl = 1;
+            pfl = dbChannelRunPreChain(dbch, pfl);
+            pfl = dbChannelRunPostChain(dbch, pfl);
+        }
+    }
+
+    status = dbChannel_get_count ( dbch, pevext->msg.m_dataType,
+                  pPayload, &item_count, pfl);
+
+    if (local_fl) db_delete_field_log(pfl);
+
     if ( status < 0 ) {
         /*
          * I cant wait to redesign this protocol from scratch!
          */
         if ( ! v41 ) {
             /*
-             * old client & plain get 
+             * old client & plain get
              * continue to return an exception
              * on failure
              */
-            send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) );
+            send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( dbch ) );
         }
         else {
             /* New clients recv the status of the operation directly to the
@@ -598,8 +617,8 @@
         }
     }
     else {
-        int cacStatus = caNetConvert ( 
-            pevext->msg.m_dataType, pPayload, pPayload, 
+        int cacStatus = caNetConvert (
+            pevext->msg.m_dataType, pPayload, pPayload,
             TRUE /* host -> net format */, item_count );
         if ( cacStatus == ECA_NORMAL ) {
             ca_uint32_t data_size =
@@ -646,6 +665,8 @@
     void *pPayload;
     int status;
     int v41;
+    int local_fl = 0;
+    db_field_log *pfl = NULL;
 
     if ( ! pciu ) {
         logBadId ( pClient, mp, 0 );
@@ -655,18 +676,18 @@
     SEND_LOCK ( pClient );
 
     if ( INVALID_DB_REQ ( mp->m_dataType ) ) {
-        send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( &pciu->addr ) );
+        send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( pciu->dbch ) );
         SEND_UNLOCK ( pClient );
         return RSRV_ERROR;
     }
 
     payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count );
-    status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, 
+    status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize,
         mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload );
     if ( status != ECA_NORMAL ) {
-        send_err ( mp, status, pClient, 
+        send_err ( mp, status, pClient,
             "server unable to load read response into protocol buffer PV=\"%s\" max bytes=%u",
-            RECORD_NAME ( &pciu->addr ), rsrvSizeofLargeBufTCP );
+            RECORD_NAME ( pciu->dbch ), rsrvSizeofLargeBufTCP );
         SEND_UNLOCK ( pClient );
         return RSRV_OK;
     }
@@ -682,25 +703,38 @@
         else{
             status = ECA_GETFAIL;
         }
-        send_err ( mp, status, 
-            pClient, RECORD_NAME ( &pciu->addr ) );
+        send_err ( mp, status,
+            pClient, RECORD_NAME ( pciu->dbch ) );
         SEND_UNLOCK ( pClient );
         return RSRV_OK;
     }
 
-    status = db_get_field ( &pciu->addr, mp->m_dataType,
-                  pPayload, mp->m_count, 0 );
+    /* If filters are involved in a read, create field log and run filters */
+    if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) {
+        pfl = db_create_read_log(pciu->dbch);
+        if (pfl) {
+            local_fl = 1;
+            pfl = dbChannelRunPreChain(pciu->dbch, pfl);
+            pfl = dbChannelRunPostChain(pciu->dbch, pfl);
+        }
+    }
+
+    status = dbChannel_get ( pciu->dbch, mp->m_dataType,
+                  pPayload, mp->m_count, pfl );
+
+    if (local_fl) db_delete_field_log(pfl);
+
     if ( status < 0 ) {
-        send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( &pciu->addr ) );
+        send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) );
         SEND_UNLOCK ( pClient );
         return RSRV_OK;
     }
 
-    status = caNetConvert ( 
-        mp->m_dataType, pPayload, pPayload, 
+    status = caNetConvert (
+        mp->m_dataType, pPayload, pPayload,
         TRUE /* host -> net format */, mp->m_count );
 	if ( status != ECA_NORMAL ) {
-        send_err ( mp, status, pClient, RECORD_NAME ( &pciu->addr ) );
+        send_err ( mp, status, pClient, RECORD_NAME ( pciu->dbch ) );
         SEND_UNLOCK ( pClient );
         return RSRV_OK;
     }
@@ -717,7 +751,7 @@
         }
         else {
             pStr[payloadSize-1] = '\0';
-            errlogPrintf ( 
+            errlogPrintf (
                 "caserver: read_action: detected DBR_STRING w/o nill termination "
                 "in response from db_get_field, pPayload = \"%s\"\n",
                 pStr );
@@ -754,11 +788,11 @@
      * favor of the standard db event calling
      * mechanism-  routine(userarg, paddr). See
      * events added above.
-     * 
+     *
      * Hold argument set true so the send message
      * buffer is not flushed once each call.
      */
-    read_reply ( &evext, &pciu->addr, TRUE, NULL );
+    read_reply ( &evext, pciu->dbch, TRUE, NULL );
 
     return RSRV_OK;
 }
@@ -766,7 +800,7 @@
 /*
  * write_action()
  */
-static int write_action ( caHdrLargeArray *mp, 
+static int write_action ( caHdrLargeArray *mp,
                         void *pPayload, struct client *client )
 {
     struct channel_in_use   *pciu;
@@ -791,25 +825,25 @@
         }
         SEND_LOCK(client);
         send_err(
-            mp, 
-            status, 
-            client, 
-            RECORD_NAME(&pciu->addr));
+            mp,
+            status,
+            client,
+            RECORD_NAME ( pciu->dbch ));
         SEND_UNLOCK(client);
         return RSRV_OK;
     }
 
-    status = caNetConvert ( 
-        mp->m_dataType, pPayload, pPayload, 
+    status = caNetConvert (
+        mp->m_dataType, pPayload, pPayload,
         FALSE /* net -> host format */, mp->m_count );
 	if ( status != ECA_NORMAL ) {
         log_header ("invalid data type", client, mp, pPayload, 0);
         SEND_LOCK(client);
         send_err(
-            mp, 
-            status, 
-            client, 
-            RECORD_NAME(&pciu->addr));
+            mp,
+            status,
+            client,
+            RECORD_NAME ( pciu->dbch ));
         SEND_UNLOCK(client);
         return RSRV_ERROR;
     }
@@ -817,10 +851,10 @@
     asWritePvt = asTrapWriteBefore ( pciu->asClientPVT,
         pciu->client->pUserName ? pciu->client->pUserName : "",
         pciu->client->pHostName ? pciu->client->pHostName : "",
-        (void *) &pciu->addr );
+        pciu->dbch );
 
-    dbStatus = db_put_field(
-                  &pciu->addr,
+    dbStatus = dbChannel_put(
+                  pciu->dbch,
                   mp->m_dataType,
                   pPayload,
                   mp->m_count);
@@ -830,10 +864,10 @@
     if (dbStatus < 0) {
         SEND_LOCK(client);
         send_err(
-            mp, 
-            ECA_PUTFAIL, 
-            client, 
-            RECORD_NAME(&pciu->addr));
+            mp,
+            ECA_PUTFAIL,
+            client,
+            RECORD_NAME ( pciu->dbch ));
         SEND_UNLOCK(client);
     }
 
@@ -852,8 +886,8 @@
     int                     chanCount;
 
     epicsMutexMustLock ( client->chanListLock );
-    chanCount = 
-        ellCount ( &client->chanList ) + 
+    chanCount =
+        ellCount ( &client->chanList ) +
         ellCount ( &client->chanPendingUpdateARList );
     epicsMutexUnlock( client->chanListLock );
 
@@ -872,13 +906,13 @@
     pName = (char *) pPayload;
     size = strlen(pName)+1;
     if (size > 512) {
-        log_header ( "bad (very long) host name", 
+        log_header ( "bad (very long) host name",
             client, mp, pPayload, 0 );
         SEND_LOCK(client);
         send_err(
-            mp, 
-            ECA_INTERNAL, 
-            client, 
+            mp,
+            ECA_INTERNAL,
+            client,
             "bad (very long) host name");
         SEND_UNLOCK(client);
         return RSRV_ERROR;
@@ -889,7 +923,7 @@
      */
     pMalloc = malloc(size);
     if(!pMalloc){
-        log_header ( "no space in pool for new host name", 
+        log_header ( "no space in pool for new host name",
             client, mp, pPayload, 0 );
         SEND_LOCK(client);
         send_err(
@@ -901,8 +935,8 @@
         return RSRV_ERROR;
     }
     strncpy(
-        pMalloc, 
-        pName, 
+        pMalloc,
+        pName,
         size-1);
     pMalloc[size-1]='\0';
 
@@ -912,13 +946,13 @@
         free ( pName );
     }
 
-    DLOG (2, ( "CAS: host_name_action for \"%s\"\n", 
+    DLOG (2, ( "CAS: host_name_action for \"%s\"\n",
         client->pHostName ? client->pHostName : "" ) );
 
     return RSRV_OK;
 }
 
-
+
 /*
  * client_name_action()
  */
@@ -931,8 +965,8 @@
     int                     chanCount;
 
     epicsMutexMustLock ( client->chanListLock );
-    chanCount = 
-        ellCount ( &client->chanList ) + 
+    chanCount =
+        ellCount ( &client->chanList ) +
         ellCount ( &client->chanPendingUpdateARList );
     epicsMutexUnlock( client->chanListLock );
 
@@ -951,13 +985,13 @@
     pName = (char *) pPayload;
     size = strlen(pName)+1;
     if (size > 512) {
-        log_header ("a very long user name was specified", 
+        log_header ("a very long user name was specified",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
         send_err(
-            mp, 
-            ECA_INTERNAL, 
-            client, 
+            mp,
+            ECA_INTERNAL,
+            client,
             "a very long user name was specified");
         SEND_UNLOCK(client);
         return RSRV_ERROR;
@@ -968,7 +1002,7 @@
      */
     pMalloc = malloc(size);
     if(!pMalloc){
-        log_header ("no memory for new user name", 
+        log_header ("no memory for new user name",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
         send_err(
@@ -980,8 +1014,8 @@
         return RSRV_ERROR;
     }
     strncpy(
-        pMalloc, 
-        pName, 
+        pMalloc,
+        pName,
         size-1);
     pMalloc[size-1]='\0';
 
@@ -999,7 +1033,7 @@
  */
 static struct channel_in_use *casCreateChannel (
 struct client   *client,
-struct dbAddr   *pAddr,
+struct dbChannel *dbch,
 unsigned    cid
 )
 {
@@ -1009,14 +1043,14 @@
     int         status;
 
     /* get block off free list if possible */
-    pchannel = (struct channel_in_use *) 
+    pchannel = (struct channel_in_use *)
         freeListCalloc(rsrvChanFreeList);
     if (!pchannel) {
         return NULL;
     }
     ellInit(&pchannel->eventq);
     epicsTimeGetCurrent(&pchannel->time_at_creation);
-    pchannel->addr = *pAddr;
+    pchannel->dbch = dbch;
     pchannel->client = client;
     /*
      * bypass read only warning
@@ -1046,8 +1080,8 @@
          * Verify that this id is not in use
          */
         status = bucketAddItemUnsignedId (
-                pCaBucket, 
-                &pchannel->sid, 
+                pCaBucket,
+                &pchannel->sid,
                 pchannel);
     } while (status == S_bucket_idInUse);
 
@@ -1116,7 +1150,7 @@
             epicsMutexUnlock ( pclient->chanListLock );
 
             /*
-             * Update all event call backs 
+             * Update all event call backs
              */
             epicsMutexMustLock(pclient->eventqLock);
             for (pevext = (struct event_ext *) ellFirst(&pciu->eventq);
@@ -1175,8 +1209,8 @@
     }
 
     SEND_LOCK ( pciu->client );
-    status = cas_copy_in_header ( 
-        pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, 
+    status = cas_copy_in_header (
+        pciu->client, CA_PROTO_ACCESS_RIGHTS, 0,
         0, 0, pciu->cid, ar, 0 );
     /*
      * OK to just ignore the request if the connection drops
@@ -1197,26 +1231,28 @@
     if ( v42 ) {
         int status;
         ca_uint32_t nElem;
+        long dbElem;
         SEND_LOCK ( pciu->client );
-        if ( pciu->addr.no_elements < 0 ) {
+        dbElem = dbChannelFinalElements(pciu->dbch);
+        if ( dbElem < 0 ) {
             nElem = 0;
         }
         else {
             if ( ! CA_V49 ( pciu->client->minor_version_number ) ) {
-                if ( pciu->addr.no_elements >= 0xffff ) {
+                if ( dbElem >= 0xffff ) {
                     nElem = 0xfffe;
                 }
                 else {
-                    nElem = (ca_uint32_t) pciu->addr.no_elements;
+                    nElem = (ca_uint32_t) dbElem;
                 }
             }
             else {
-                nElem = (ca_uint32_t) pciu->addr.no_elements;
+                nElem = (ca_uint32_t) dbElem;
             }
         }
-        status = cas_copy_in_header ( 
+        status = cas_copy_in_header (
             pciu->client, CA_PROTO_CREATE_CHAN, 0u,
-            pciu->addr.dbr_field_type, nElem, pciu->cid, 
+            dbChannelFinalExportType(pciu->dbch), nElem, pciu->cid,
             pciu->sid, NULL );
         if ( status == ECA_NORMAL ) {
             cas_commit_msg ( pciu->client, 0u );
@@ -1228,7 +1264,7 @@
 /*
  * claim_ciu_action()
  */
-static int claim_ciu_action ( caHdrLargeArray *mp, 
+static int claim_ciu_action ( caHdrLargeArray *mp,
                             void *pPayload, client *client )
 {
     int status;
@@ -1243,47 +1279,48 @@
     client->minor_version_number = mp->m_available;
 
     if (CA_V44(client->minor_version_number)) {
-        struct dbAddr  tmp_addr;
+        struct dbChannel *dbch;
         char *pName = (char *) pPayload;
 
         /*
          * check the sanity of the message
          */
         if (mp->m_postsize<=1) {
-            log_header ( "empty PV name in UDP search request?", 
+            log_header ( "empty PV name in UDP search request?",
                 client, mp, pPayload, 0 );
             return RSRV_OK;
         }
         pName[mp->m_postsize-1] = '\0';
 
-        status = db_name_to_addr (pName, &tmp_addr);
-        if (status) {
+        dbch = dbChannel_create (pName);
+        if (!dbch) {
             return RSRV_OK;
         }
 
-        DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", 
-            pName, tmp_addr.dbr_field_type, tmp_addr.no_elements) );
-        
+        DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n",
+            pName, dbChannelExportType(dbch), dbChannelElements(dbch)) );
+
         pciu = casCreateChannel (
-                client, 
-                &tmp_addr, 
+                client,
+                dbch,
                 mp->m_cid);
         if (!pciu) {
-            log_header ("no memory to create new channel", 
+            log_header ("no memory to create new channel",
                 client, mp, pPayload, 0);
             SEND_LOCK(client);
-            send_err(mp, 
-                ECA_ALLOCMEM, 
-                client, 
-                RECORD_NAME(&tmp_addr));
+            send_err(mp,
+                ECA_ALLOCMEM,
+                client,
+                RECORD_NAME(dbch));
             SEND_UNLOCK(client);
+            dbChannelDelete(dbch);
             return RSRV_ERROR;
         }
     }
     else {
         epicsMutexMustLock(prsrv_cast_client->chanListLock);
         /*
-         * clients which dont claim their 
+         * clients which dont claim their
          * channel in use block prior to
          * timeout must reconnect
          */
@@ -1302,8 +1339,8 @@
             return RSRV_ERROR;
         }
 
-        /* 
-         * duplicate claim message are unacceptable 
+        /*
+         * duplicate claim message are unacceptable
          * (so we disconnect the client)
          */
         if (pciu->client!=prsrv_cast_client) {
@@ -1327,7 +1364,7 @@
          * who is claiming it
          */
         ellDelete(
-            &prsrv_cast_client->chanList, 
+            &prsrv_cast_client->chanList,
             &pciu->node);
         epicsMutexUnlock(prsrv_cast_client->chanListLock);
 
@@ -1343,12 +1380,12 @@
      */
     status = asAddClient(
             &pciu->asClientPVT,
-            asDbGetMemberPvt(&pciu->addr),
-            asDbGetAsl(&pciu->addr),
+            asDbGetMemberPvt(pciu->dbch),
+            asDbGetAsl(pciu->dbch),
             client->pUserName ? client->pUserName : "",
-            client->pHostName ? client->pHostName : ""); 
+            client->pHostName ? client->pHostName : "");
     if(status != 0 && status != S_asLib_asNotActive){
-        log_header ("No room for security table", 
+        log_header ("No room for security table",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
         send_err(mp, ECA_ALLOCMEM, client, "No room for security table");
@@ -1357,7 +1394,7 @@
     }
 
     /*
-     * store ptr to channel in use block 
+     * store ptr to channel in use block
      * in access security private
      */
     asPutClientPvt(pciu->asClientPVT, pciu);
@@ -1366,7 +1403,7 @@
      * register for asynch updates of access rights changes
      */
     status = asRegisterClientCallback(
-            pciu->asClientPVT, 
+            pciu->asClientPVT,
             casAccessRightsCB);
     if ( status == S_asLib_asNotActive ) {
         epicsMutexMustLock ( client->chanListLock );
@@ -1378,10 +1415,10 @@
         claim_ciu_reply ( pciu );
     }
     else if (status!=0) {
-        log_header ("No room for access security state change subscription", 
+        log_header ("No room for access security state change subscription",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
-        send_err(mp, ECA_ALLOCMEM, client, 
+        send_err(mp, ECA_ALLOCMEM, client,
             "No room for access security state change subscription");
         SEND_UNLOCK(client);
         return RSRV_ERROR;
@@ -1429,7 +1466,7 @@
     db_post_extra_labor(pClient->evuser);
 }
 
-/* 
+/*
  * write_notify_reply()
  * (called by the CA server event task via the extra labor interface)
  */
@@ -1477,18 +1514,18 @@
         asTrapWriteAfter ( asWritePvtTmp );
 
         /*
-         * the channel id field is being abused to carry 
+         * the channel id field is being abused to carry
          * status here
          */
         SEND_LOCK(pClient);
-        localStatus = cas_copy_in_header ( 
-            pClient, CA_PROTO_WRITE_NOTIFY, 
-            0u, msgtmp.m_dataType, msgtmp.m_count, status, 
+        localStatus = cas_copy_in_header (
+            pClient, CA_PROTO_WRITE_NOTIFY,
+            0u, msgtmp.m_dataType, msgtmp.m_count, status,
             msgtmp.m_available, 0 );
         if ( localStatus != ECA_NORMAL ) {
             /*
              * inability to aquire buffer space
-             * Indicates corruption  
+             * Indicates corruption
              */
             errlogPrintf("CA server corrupted - put call back(s) discarded\n");
             SEND_UNLOCK ( pClient );
@@ -1511,11 +1548,11 @@
  */
 static void sendAllUpdateAS ( struct client *client )
 {
-    struct channel_in_use *pciu; 
+    struct channel_in_use *pciu;
 
     epicsMutexMustLock ( client->chanListLock );
 
-    pciu = ( struct channel_in_use * ) 
+    pciu = ( struct channel_in_use * )
         ellGet ( & client->chanPendingUpdateARList );
     while ( pciu ) {
         if ( pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) {
@@ -1531,14 +1568,14 @@
         }
         pciu->state = rsrvCS_inService;
         ellAdd ( & client->chanList, & pciu->node );
-        pciu = ( struct channel_in_use * ) 
+        pciu = ( struct channel_in_use * )
             ellGet ( & client->chanPendingUpdateARList );
     }
 
     epicsMutexUnlock( client->chanListLock );
 }
 
-/* 
+/*
  * rsrv_extra_labor()
  * (called by the CA server event task via the extra labor interface)
  */
@@ -1562,8 +1599,8 @@
      * the cid field abused to contain status
      * during put cb replies
      */
-    status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, 
-        0u, mp->m_dataType, mp->m_count, statusCA, 
+    status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY,
+        0u, mp->m_dataType, mp->m_count, statusCA,
         mp->m_available, 0 );
     if ( status != ECA_NORMAL ) {
         SEND_UNLOCK ( client );
@@ -1578,28 +1615,28 @@
 void initializePutNotifyFreeList (void)
 {
     if ( ! rsrvPutNotifyFreeList ) {
-        freeListInitPvt ( &rsrvPutNotifyFreeList, 
+        freeListInitPvt ( &rsrvPutNotifyFreeList,
             sizeof(struct rsrv_put_notify), 512 );
         assert ( rsrvPutNotifyFreeList );
     }
 }
 
-static struct rsrv_put_notify * 
+static struct rsrv_put_notify *
     rsrvAllocPutNotify ( struct channel_in_use * pciu )
 {
     struct rsrv_put_notify *pNotify;
-    
+
     if ( rsrvPutNotifyFreeList ) {
         pNotify = (RSRVPUTNOTIFY *)
             freeListCalloc ( rsrvPutNotifyFreeList );
         if ( pNotify ) {
-            pNotify->dbPutNotify.pbuffer = 
+            pNotify->dbPutNotify.pbuffer =
                     &pNotify->dbrScalarValue;
-            pNotify->valueSize = 
+            pNotify->valueSize =
                     sizeof (pNotify->dbrScalarValue);
             pNotify->dbPutNotify.usrPvt = pciu;
-            pNotify->dbPutNotify.paddr = &pciu->addr;
-            pNotify->dbPutNotify.userCallback = 
+            pNotify->dbPutNotify.chan = pciu->dbch;
+            pNotify->dbPutNotify.userCallback =
                     write_notify_call_back;
         }
     }
@@ -1609,18 +1646,18 @@
     return pNotify;
 }
 
-static int rsrvExpandPutNotify ( 
+static int rsrvExpandPutNotify (
     struct rsrv_put_notify * pNotify, unsigned sizeNeeded )
 {
     int booleanStatus;
-    
+
     if ( sizeNeeded > pNotify->valueSize ) {
         /*
-         * try to use the union embeded in the free list 
-         * item, but allocate a random sized block if they 
+         * try to use the union embeded in the free list
+         * item, but allocate a random sized block if they
          * writing a vector.
          */
-        if ( pNotify->valueSize > 
+        if ( pNotify->valueSize >
             sizeof (pNotify->dbrScalarValue) ) {
             free ( pNotify->dbPutNotify.pbuffer );
         }
@@ -1633,9 +1670,9 @@
             /*
              * revert back to the embedded union
              */
-            pNotify->dbPutNotify.pbuffer = 
+            pNotify->dbPutNotify.pbuffer =
                 &pNotify->dbrScalarValue;
-            pNotify->valueSize = 
+            pNotify->valueSize =
                     sizeof (pNotify->dbrScalarValue);
             booleanStatus = FALSE;
         }
@@ -1643,7 +1680,7 @@
     else {
         booleanStatus = TRUE;
     }
-    
+
     return booleanStatus;
 }
 
@@ -1651,7 +1688,7 @@
 {
     unsigned size = sizeof ( *pNotify );
     if ( pNotify ) {
-        if ( pNotify->valueSize > 
+        if ( pNotify->valueSize >
                 sizeof ( pNotify->dbrScalarValue ) ) {
             size += pNotify->valueSize;
         }
@@ -1659,7 +1696,7 @@
     return size;
 }
 
-void rsrvFreePutNotify ( client *pClient, 
+void rsrvFreePutNotify ( client *pClient,
         struct rsrv_put_notify *pNotify )
 {
      if ( pNotify ) {
@@ -1671,16 +1708,16 @@
         epicsMutexUnlock ( pClient->putNotifyLock );
 
         /*
-         * if any possiblity that the put notify is 
+         * if any possiblity that the put notify is
          * outstanding then cancel it
          */
         if ( busyTmp ) {
             dbNotifyCancel ( &pNotify->dbPutNotify );
-        } 
+        }
 
         epicsMutexMustLock ( pClient->putNotifyLock );
         if ( pNotify->onExtraLaborQueue ) {
-            ellDelete ( &pClient->putNotifyQue, 
+            ellDelete ( &pClient->putNotifyQue,
                         &pNotify->node );
         }
         busyTmp = pNotify->busy;
@@ -1692,7 +1729,7 @@
             asTrapWriteAfter ( asWritePvtTmp );
         }
 
-        if ( pNotify->valueSize > 
+        if ( pNotify->valueSize >
                 sizeof(pNotify->dbrScalarValue) ) {
             free ( pNotify->dbPutNotify.pbuffer );
         }
@@ -1703,7 +1740,7 @@
 /*
  * write_notify_action()
  */
-static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, 
+static int write_notify_action ( caHdrLargeArray *mp, void *pPayload,
                                struct client  *client )
 {
     unsigned size;
@@ -1732,7 +1769,7 @@
     if ( pciu->pPutNotify ) {
 
         /*
-         * serialize concurrent put notifies 
+         * serialize concurrent put notifies
          */
         epicsMutexMustLock(client->putNotifyLock);
         while(pciu->pPutNotify->busy){
@@ -1757,7 +1794,7 @@
                 busyTmp = pciu->pPutNotify->busy;
                 if ( busyTmp ) {
                     if ( pciu->pPutNotify->onExtraLaborQueue ) {
-                        ellDelete ( &client->putNotifyQue, 
+                        ellDelete ( &client->putNotifyQue,
                                     &pciu->pPutNotify->node );
                     }
                     pciu->pPutNotify->busy = FALSE;
@@ -1765,9 +1802,9 @@
                     pciu->pPutNotify->asWritePvt = 0;
                 }
                 epicsMutexUnlock(client->putNotifyLock);
-                
+
                 if ( busyTmp ) {
-                    log_header("put call back time out", client, 
+                    log_header("put call back time out", client,
                         &pciu->pPutNotify->msg, pciu->pPutNotify->dbPutNotify.pbuffer, 0);
                     asTrapWriteAfter ( asWritePvtTmp );
                     putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG);
@@ -1784,15 +1821,15 @@
              * send error and go to next request
              * if there isnt enough memory left
              */
-            log_header ( "no memory to initiate put notify", 
+            log_header ( "no memory to initiate put notify",
                 client, mp, pPayload, 0 );
             putNotifyErrorReply (client, mp, ECA_ALLOCMEM);
             return RSRV_ERROR;
         }
     }
-    
+
     if ( ! rsrvExpandPutNotify ( pciu->pPutNotify, size ) ) {
-        log_header ( "no memory to initiate vector put notify", 
+        log_header ( "no memory to initiate vector put notify",
             client, mp, pPayload, 0 );
         putNotifyErrorReply ( client, mp, ECA_ALLOCMEM );
         return RSRV_ERROR;
@@ -1803,8 +1840,8 @@
     pciu->pPutNotify->msg = *mp;
     pciu->pPutNotify->dbPutNotify.nRequest = mp->m_count;
 
-    status = caNetConvert ( 
-        mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, 
+    status = caNetConvert (
+        mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer,
         FALSE /* net -> host format */, mp->m_count );
 	if ( status != ECA_NORMAL ) {
         log_header ("invalid data type", client, mp, pPayload, 0);
@@ -1819,11 +1856,11 @@
         return RSRV_OK;
     }
 
-    pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( 
+    pciu->pPutNotify->asWritePvt = asTrapWriteBefore (
         pciu->asClientPVT,
         pciu->client->pUserName ? pciu->client->pUserName : "",
         pciu->client->pHostName ? pciu->client->pHostName : "",
-        (void *) &pciu->addr );
+        pciu->dbch );
 
     dbPutNotify(&pciu->pPutNotify->dbPutNotify);
 
@@ -1849,10 +1886,10 @@
     }
 
     /*
-     * stop further use of server if memory becomes scarse
+     * stop further use of server if memory becomes scarce
      */
     spaceAvailOnFreeList = freeListItemsAvail ( rsrvEventFreeList ) > 0;
-    if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { 
+    if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) {
         pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList);
     }
     else {
@@ -1860,14 +1897,14 @@
     }
 
     if (!pevext) {
-        log_header ("no memory to add subscription", 
+        log_header ("no memory to add subscription",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
         send_err(
             mp,
-            ECA_ALLOCMEM, 
-            client, 
-            RECORD_NAME(&pciu->addr));
+            ECA_ALLOCMEM,
+            client,
+            RECORD_NAME(pciu->dbch));
         SEND_UNLOCK(client);
         return RSRV_ERROR;
     }
@@ -1881,15 +1918,15 @@
     ellAdd( &pciu->eventq, &pevext->node);
     epicsMutexUnlock(client->eventqLock);
 
-    pevext->pdbev = db_add_event (client->evuser, &pciu->addr,
+    pevext->pdbev = db_add_event (client->evuser, pciu->dbch,
                 read_reply, pevext, pevext->mask);
     if (pevext->pdbev == NULL) {
-        log_header ("no memory to add subscription to db", 
+        log_header ("no memory to add subscription to db",
             client, mp, pPayload, 0);
         SEND_LOCK(client);
-        send_err (mp, ECA_ALLOCMEM, client, 
-            "subscription install into record %s failed", 
-            RECORD_NAME(&pciu->addr));
+        send_err (mp, ECA_ALLOCMEM, client,
+            "subscription install into record %s failed",
+            RECORD_NAME(pciu->dbch));
         SEND_UNLOCK(client);
         return RSRV_ERROR;
     }
@@ -1903,7 +1940,7 @@
      * of the socket would block. This prevents
      * a application program initiated deadlock.
      *
-     * However when I am reconnecting I reissue 
+     * However when I am reconnecting I reissue
      * the monitors and I could get deadlocked.
      * The client is blocked sending and the server
      * task for the client is blocked sending in
@@ -1914,7 +1951,7 @@
      * post_single_event() below instead of calling
      * read_reply() in this module. This is a complete
      * fix since a monitor setup is the only request
-     * soliciting a reply in the client which is 
+     * soliciting a reply in the client which is
      * issued from inside of service.c (from inside
      * of the part of the ca client which services
      * messages sent by the server).
@@ -1940,13 +1977,13 @@
 /*
  *  clear_channel_reply()
  */
-static int clear_channel_reply ( caHdrLargeArray *mp, 
+static int clear_channel_reply ( caHdrLargeArray *mp,
         void *pPayload, struct client  *client )
 {
      struct event_ext *pevext;
      struct channel_in_use *pciu;
      int status;
-     
+
      /*
       *
       * Verify the channel
@@ -1957,43 +1994,43 @@
          logBadId ( client, mp, pPayload );
          return RSRV_ERROR;
      }
-     
+
      rsrvFreePutNotify ( client, pciu->pPutNotify );
-     
+
      while (TRUE){
          epicsMutexMustLock(client->eventqLock);
          pevext = (struct event_ext *) ellGet(&pciu->eventq);
          epicsMutexUnlock(client->eventqLock);
-         
+
          if(!pevext){
              break;
          }
-         
+
          if (pevext->pdbev) {
              db_cancel_event (pevext->pdbev);
          }
          freeListFree(rsrvEventFreeList, pevext);
      }
-     
+
      db_flush_extra_labor_event ( client->evuser );
-          
+
      /*
       * send delete confirmed message
       */
      SEND_LOCK(client);
-     status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, 
-        0u, mp->m_dataType, mp->m_count, mp->m_cid, 
+     status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL,
+        0u, mp->m_dataType, mp->m_count, mp->m_cid,
         mp->m_available, NULL );
      if ( status != ECA_NORMAL ) {
         SEND_UNLOCK(client);
         return RSRV_ERROR;
      }
-     
+
      cas_commit_msg ( client, 0u );
      SEND_UNLOCK(client);
-     
+
      epicsMutexMustLock ( client->chanListLock );
-     if ( pciu->state == rsrvCS_inService || 
+     if ( pciu->state == rsrvCS_inService ||
             pciu->state == rsrvCS_pendConnectResp  ) {
         ellDelete ( &client->chanList, &pciu->node );
      }
@@ -2004,22 +2041,22 @@
      else {
         epicsMutexUnlock( client->chanListLock );
         SEND_LOCK(client);
-        send_err(mp, ECA_INTERNAL, client, 
+        send_err(mp, ECA_INTERNAL, client,
             "channel was in strange state or corrupted during cleanup");
         SEND_UNLOCK(client);
         return RSRV_ERROR;
      }
      epicsMutexUnlock( client->chanListLock );
-     
+
      /*
       * remove from access control list
       */
      status = asRemoveClient(&pciu->asClientPVT);
      if(status != 0 && status != S_asLib_asNotActive){
-         errMessage(status, RECORD_NAME(&pciu->addr));
+         errMessage(status, RECORD_NAME(pciu->dbch));
          return RSRV_ERROR;
      }
-     
+
      LOCK_CLIENTQ;
      status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid);
      if(status != S_bucket_success){
@@ -2031,11 +2068,12 @@
      rsrvChannelCount--;
      UNLOCK_CLIENTQ;
 
+     dbChannelDelete(pciu->dbch);
      freeListFree(rsrvChanFreeList, pciu);
-     
+
      return RSRV_OK;
 }
- 
+
 /*
  *
  *  event_cancel_reply()
@@ -2049,7 +2087,7 @@
      struct channel_in_use  *pciu;
      struct event_ext       *pevext;
      int                    status;
-     
+
      /*
       *
       * Verify the channel
@@ -2060,7 +2098,7 @@
          logBadId ( client, mp, pPayload );
          return RSRV_ERROR;
      }
-     
+
      /*
       * search events on this channel for a match
       * (there are usually very few monitors per channel)
@@ -2068,38 +2106,38 @@
      epicsMutexMustLock(client->eventqLock);
      for (pevext = (struct event_ext *) ellFirst(&pciu->eventq);
             pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){
-         
+
          if (pevext->msg.m_available == mp->m_available) {
              ellDelete(&pciu->eventq, &pevext->node);
              break;
          }
      }
      epicsMutexUnlock(client->eventqLock);
-     
+
      /*
-      * Not Found- return an exception event 
+      * Not Found- return an exception event
       */
      if(!pevext){
          SEND_LOCK(client);
-         send_err(mp, ECA_BADMONID, client, RECORD_NAME(&pciu->addr));
+         send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch));
          SEND_UNLOCK(client);
          return RSRV_ERROR;
      }
-     
+
      /*
       * cancel monitor activity in progress
       */
      if (pevext->pdbev) {
          db_cancel_event (pevext->pdbev);
      }
-     
+
      /*
       * send delete confirmed message
       */
      SEND_LOCK(client);
 
-     status = cas_copy_in_header ( client, pevext->msg.m_cmmd, 
-        0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, 
+     status = cas_copy_in_header ( client, pevext->msg.m_cmmd,
+        0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid,
         pevext->msg.m_available, NULL );
      if ( status != ECA_NORMAL ) {
          SEND_UNLOCK(client);
@@ -2107,7 +2145,7 @@
      }
      cas_commit_msg ( client, 0 );
      SEND_UNLOCK(client);
-     
+
      freeListFree (rsrvEventFreeList, pevext);
 
      return RSRV_OK;
@@ -2120,8 +2158,8 @@
 {
     int status;
     SEND_LOCK(client);
-    status = cas_copy_in_header ( client, mp->m_cmmd, 
-        0u, mp->m_dataType, mp->m_count, mp->m_cid, 
+    status = cas_copy_in_header ( client, mp->m_cmmd,
+        0u, mp->m_dataType, mp->m_count, mp->m_cid,
         mp->m_available, NULL );
     if ( status != ECA_NORMAL ) {
         SEND_UNLOCK(client);
@@ -2132,17 +2170,17 @@
     return RSRV_OK;
 }
 
-/*  
+/*
  *  search_fail_reply()
  *
- *  Only when requested by the client 
+ *  Only when requested by the client
  *  send search failed reply
  */
 static void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client)
 {
     int status;
     SEND_LOCK ( client );
-    status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, 
+    status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND,
         0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL );
     if ( status != ECA_NORMAL ) {
         SEND_UNLOCK ( client );
@@ -2180,16 +2218,16 @@
     SEND_LOCK ( client );
     /*
      * sequence number is specified zero when we copy in the
-     * header because we dont know it until we receive a datagram 
+     * header because we dont know it until we receive a datagram
      * from the client
      */
-    status = cas_copy_in_header ( client, CA_PROTO_VERSION, 
-        0, 0, CA_MINOR_PROTOCOL_REVISION, 
+    status = cas_copy_in_header ( client, CA_PROTO_VERSION,
+        0, 0, CA_MINOR_PROTOCOL_REVISION,
         0, 0, 0 );
     if ( status != ECA_NORMAL ) {
         SEND_UNLOCK ( client );
         return RSRV_ERROR;
-    }        
+    }
     cas_commit_msg ( client, 0 );
     SEND_UNLOCK ( client );
     return RSRV_OK;
@@ -2200,7 +2238,6 @@
  */
 static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client *client )
 {
-    struct dbAddr   tmp_addr;
     ca_uint16_t     *pMinorVersion;
     char            *pName = (char *) pPayload;
     int             status;
@@ -2215,41 +2252,41 @@
      * check the sanity of the message
      */
     if (mp->m_postsize<=1) {
-        log_header ("empty PV name in UDP search request?", 
+        log_header ("empty PV name in UDP search request?",
             client, mp, pPayload, 0);
         return RSRV_OK;
     }
     pName[mp->m_postsize-1] = '\0';
-    
+
     /* Exit quickly if channel not on this node */
-    status = db_name_to_addr (pName, &tmp_addr);
-    if (status) {
+    if (dbChannelTest(pName)) {
         DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) );
         if (mp->m_dataType == DOREPLY)
             search_fail_reply ( mp, pPayload, client );
         return RSRV_OK;
     }
-    
+
     /*
-     * stop further use of server if memory becomes scarse
+     * stop further use of server if memory becomes scarce
      */
     spaceAvailOnFreeList =     freeListItemsAvail ( rsrvChanFreeList ) > 0
                             && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace;
-    spaceNeeded = sizeof (struct channel_in_use) + 
+    spaceNeeded = sizeof (struct channel_in_use) +
         reasonableMonitorSpace * sizeof (struct event_ext);
-    if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { 
+    if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) {
         SEND_LOCK(client);
         send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" );
         SEND_UNLOCK(client);
         return RSRV_OK;
     }
-    
+
     /*
      * starting with V4.4 the count field is used (abused)
      * to store the minor version number of the client.
      *
      * New versions dont alloc the channel in response
      * to a search request.
+     * For these, allocation has been moved to claim_ciu_action().
      *
      * m_count, m_cid are already in host format...
      */
@@ -2259,49 +2296,58 @@
         type = ca_server_port;
     }
     else {
+        struct dbChannel *dbch;
         struct channel_in_use   *pchannel;
-        
-        pchannel = casCreateChannel ( client, &tmp_addr, mp->m_cid );
+
+        dbch = dbChannel_create(pName);
+        if (!dbch) {
+            DLOG ( 2, ( "CAS: dbChannel Test of \"%s\" OK but Create failed\n", pName ) );
+            if (mp->m_dataType == DOREPLY)
+                search_fail_reply ( mp, pPayload, client );
+            return RSRV_OK;
+        }
+        pchannel = casCreateChannel ( client, dbch, mp->m_cid );
         if (!pchannel) {
             SEND_LOCK(client);
-            send_err ( mp, ECA_ALLOCMEM, client, 
-                RECORD_NAME ( &tmp_addr ) );
+            send_err ( mp, ECA_ALLOCMEM, client,
+                RECORD_NAME ( dbch ) );
             SEND_UNLOCK ( client );
+            dbChannelDelete(dbch);
             return RSRV_OK;
         }
         sid = pchannel->sid;
-        if ( tmp_addr.no_elements < 0 ) {
+        if ( dbChannelFinalElements(dbch) < 0 ) {
             count = 0;
         }
-        else if ( tmp_addr.no_elements > 0xffff ) {
+        else if ( dbChannelFinalElements(dbch) > 0xffff ) {
             count = 0xfffe;
         }
         else {
-            count = (ca_uint16_t) tmp_addr.no_elements;
+            count = (ca_uint16_t) dbChannelFinalElements(dbch);
         }
-        type = (ca_uint16_t) tmp_addr.dbr_field_type;
+        type = (ca_uint16_t) dbChannelFinalExportType(dbch);
     }
-    
+
     SEND_LOCK ( client );
-    status = cas_copy_in_header ( client, CA_PROTO_SEARCH, 
-        sizeof(*pMinorVersion), type, count, 
-        sid, mp->m_available, 
+    status = cas_copy_in_header ( client, CA_PROTO_SEARCH,
+        sizeof(*pMinorVersion), type, count,
+        sid, mp->m_available,
         ( void * ) &pMinorVersion );
     if ( status != ECA_NORMAL ) {
         SEND_UNLOCK ( client );
         return RSRV_ERROR;
     }
-    
+
     /*
      * Starting with CA V4.1 the minor version number
      * is appended to the end of each search reply.
-     * This value is ignored by earlier clients. 
+     * This value is ignored by earlier clients.
      */
     *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION );
-    
+
     cas_commit_msg ( client, sizeof ( *pMinorVersion ) );
     SEND_UNLOCK ( client );
-    
+
     return RSRV_OK;
 }
 
@@ -2311,7 +2357,6 @@
 static int search_reply_tcp ( 
     caHdrLargeArray *mp, void *pPayload, struct client *client )
 {
-    struct dbAddr   tmp_addr;
     char            *pName = (char *) pPayload;
     int             status;
     int             spaceAvailOnFreeList;
@@ -2329,8 +2374,7 @@
     pName[mp->m_postsize-1] = '\0';
 
     /* Exit quickly if channel not on this node */
-    status = db_name_to_addr (pName, &tmp_addr);
-    if (status) {
+    if (dbChannelTest(pName)) {
         DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) );
       

Replies:
Re: [Merge] lp:~epics-core/epics-base/server-side-plugins into lp:epics-base Andrew Johnson
[Merge] lp:~epics-core/epics-base/server-side-plugins into lp:epics-base noreply

Navigate by Date:
Prev: Re: R3.15.0 deadline approaching Michael Davidsaver
Next: [Merge] lp:~epics-core/epics-base/thread-pool into lp:epics-base mdavidsaver
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: R3.15.0 deadline approaching Michael Davidsaver
Next: Re: [Merge] lp:~epics-core/epics-base/server-side-plugins into lp:epics-base Andrew Johnson
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024