diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 0479e8a3a..39a1a79cf 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -97,6 +97,14 @@ be happy to try and answer them!
+Output from dbpr command enhanced
+
+The "DataBase Print Record" command dbpr now generates slightly
+better output, with more field types having their own display methods. This
+release also includes additional protection against buffer overflows while
+printing long links in dbpr, and corrects the output of long strings
+from the dbgf command.
+
Record types mbbiDirect and mbboDirect extended to 32 bit
The VAL fields of mbbiDirect and mbboDirect records have
@@ -752,7 +760,16 @@ of its CALLBACK objects.
+HOWTO: Converting Wiki Record Reference to POD
+
+Some documentation has been added to the dbdToHtml.pl script
+explaining how Perl POD (Plain Old Documentation) markup can be added to
+.dbd files to generate HTML documentation for the record types. To see
+these instructions, run perl bin/<host>/dbdToHtml.pl -H
+or perldoc bin/<host>/dbdToHtml.pl.
+
Fix problem with numeric soft events
+
Changing from numeric to named soft events introduced an incompatibility
when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record.
The post_event() API is not marked deprecated any more.
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 72ad6cd24..e844ad826 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -784,11 +784,16 @@ static void connectionCallback(struct connection_handler_args arg)
if (pca->gotFirstConnection) {
if (pca->nelements != ca_element_count(arg.chid) ||
pca->dbrType != ca_field_type(arg.chid)) {
- /* BUG: We have no way to clear any old subscription with the
- * originally chosen data type/size. That will continue
- * to send us data and will result in an assert() fail.
- */
- /* Let next dbCaGetLink and/or dbCaPutLink determine options */
+ /* Size or type changed, clear everything and let the next call
+ to dbCaGetLink() and/or dbCaPutLink() reset everything */
+ if (pca->evidNative) {
+ ca_clear_event(pca->evidNative);
+ pca->evidNative = 0;
+ }
+ if (pca->evidString) {
+ ca_clear_event(pca->evidString);
+ pca->evidString = 0;
+ }
plink->value.pv_link.pvlMask &=
~(pvlOptInpNative | pvlOptInpString |
pvlOptOutNative | pvlOptOutString);
@@ -1149,7 +1154,8 @@ static void dbCaTask(void *arg)
status = ca_add_array_event(
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
0, /* dynamic size */
- pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
+ pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
+ &pca->evidNative);
if (status != ECA_NORMAL) {
errlogPrintf("dbCaTask ca_add_array_event %s\n",
ca_message(status));
@@ -1161,7 +1167,8 @@ static void dbCaTask(void *arg)
pca->pgetString = dbCalloc(1, MAX_STRING_SIZE);
epicsMutexUnlock(pca->lock);
status = ca_add_array_event(DBR_TIME_STRING, 1,
- pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
+ pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
+ &pca->evidString);
if (status != ECA_NORMAL) {
errlogPrintf("dbCaTask ca_add_array_event %s\n",
ca_message(status));
diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h
index 454ead5ac..c6bdf359b 100644
--- a/modules/database/src/ioc/db/dbCaPvt.h
+++ b/modules/database/src/ioc/db/dbCaPvt.h
@@ -80,6 +80,8 @@ typedef struct caLink
char *pgetString;
void *pputNative;
char *pputString;
+ evid evidNative;
+ evid evidString;
char gotInNative;
char gotInString;
char gotOutNative;
diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
index 4bbd40630..cb9963ef3 100644
--- a/modules/database/src/ioc/db/dbTest.c
+++ b/modules/database/src/ioc/db/dbTest.c
@@ -42,12 +42,13 @@
#include "special.h"
#define MAXLINE 80
+#define MAXMESS 128
struct msgBuff { /* line output structure */
char out_buff[MAXLINE + 1];
char *pNext;
char *pLast;
char *pNexTab;
- char message[128];
+ char message[MAXMESS];
};
typedef struct msgBuff TAB_BUFFER;
@@ -963,7 +964,7 @@ static void printBuffer(
int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len;
sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i);
- len -= chunk;
+ len -= chunk; i += chunk;
if (len > 0)
strcat(pmsg, " +");
dbpr_msgOut(pMsgBuff, tab_size);
@@ -1129,7 +1130,7 @@ static int dbpr_report(
case DBF_DEVICE:
status = dbFindField(pdbentry,pfield_name);
pfield_value = dbGetString(pdbentry);
- sprintf(pmsg, "%s: %s", pfield_name,
+ sprintf(pmsg, "%-4s: %s", pfield_name,
(pfield_value ? pfield_value : ""));
dbpr_msgOut(pMsgBuff, tab_size);
break;
@@ -1139,19 +1140,18 @@ static int dbpr_report(
case DBF_FWDLINK: {
DBLINK *plink = (DBLINK *)pfield;
int ind;
+ const char *type = "LINK";
status = dbFindField(pdbentry,pfield_name);
- for (ind=0; indtype)
- break;
- }
- if (ind>=LINK_NTYPES) {
- sprintf(pmsg,"%s: Illegal Link Type", pfield_name);
- }
- else {
- sprintf(pmsg,"%s:%s %s", pfield_name,
- pamaplinkType[ind].strvalue,dbGetString(pdbentry));
- }
+ if (!plink->text)
+ for (ind=0; indtype) {
+ type = pamaplinkType[ind].strvalue;
+ break;
+ }
+ }
+ epicsSnprintf(pmsg, MAXMESS, "%-4s: %s %s", pfield_name,
+ type, dbGetString(pdbentry));
dbpr_msgOut(pMsgBuff, tab_size);
}
break;
@@ -1162,13 +1162,21 @@ static int dbpr_report(
char time_buf[40];
epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f",
&paddr->precord->time);
- sprintf(pmsg, "%s: %s", pfield_name, time_buf);
+ sprintf(pmsg, "%-4s: %s", pfield_name, time_buf);
dbpr_msgOut(pMsgBuff, tab_size);
}
else if (pdbFldDes->size == sizeof(void *) &&
strchr(pdbFldDes->extra, '*')) {
- /* Special for pointers, needed on little-endian CPUs */
- sprintf(pmsg, "%s: %p", pfield_name, *(void **)pfield);
+ /* Special for pointers */
+ sprintf(pmsg, "%-4s: PTR %p", pfield_name, *(void **)pfield);
+ dbpr_msgOut(pMsgBuff, tab_size);
+ }
+ else if (pdbFldDes->size == sizeof(ELLLIST) &&
+ !strncmp(pdbFldDes->extra, "ELLLIST", 7)) {
+ /* Special for linked lists */
+ ELLLIST *plist = (ELLLIST *)pfield;
+ sprintf(pmsg, "%-4s: ELL %d [%p .. %p]", pfield_name,
+ ellCount(plist), ellFirst(plist), ellLast(plist));
dbpr_msgOut(pMsgBuff, tab_size);
}
else { /* just print field as hex bytes */
@@ -1184,7 +1192,7 @@ static int dbpr_report(
value = (unsigned int)*pchar;
sprintf(ptemp_buf, "%02x ", value);
}
- sprintf(pmsg, "%s: %s", pfield_name,temp_buf);
+ sprintf(pmsg, "%-4s: %s", pfield_name,temp_buf);
dbpr_msgOut(pMsgBuff, tab_size);
}
break;
diff --git a/modules/database/src/tools/Makefile b/modules/database/src/tools/Makefile
index 2aef4d5c0..d61727661 100644
--- a/modules/database/src/tools/Makefile
+++ b/modules/database/src/tools/Makefile
@@ -35,4 +35,6 @@ PERL_SCRIPTS += dbExpand.pl
PERL_SCRIPTS += dbdToHtml.pl
PERL_SCRIPTS += registerRecordDeviceDriver.pl
+HTMLS += dbdToHtml.html
+
include $(TOP)/configure/RULES
diff --git a/modules/database/src/tools/dbdToHtml.pl b/modules/database/src/tools/dbdToHtml.pl
index 542e140cf..c51c793b5 100644
--- a/modules/database/src/tools/dbdToHtml.pl
+++ b/modules/database/src/tools/dbdToHtml.pl
@@ -44,17 +44,67 @@ BEGIN {
}
}
-my $tool = 'dbdToHtml';
+use Pod::Usage;
-our ($opt_D, @opt_I, $opt_o);
-getopts('DI@o:') or
- die "Usage: $tool [-D] [-I dir] [-o file.html] file.dbd.pod\n";
+=head1 NAME
+
+dbdToHtml.pl - Convert DBD file with POD to HTML
+
+=head1 SYNOPSIS
+
+B [B<-h>] [B<-D>] [B<-I> dir] [B<-o> file] file.dbd.pod
+
+=head1 DESCRIPTION
+
+Generates HTML documentation from a B<.dbd.pod> file.
+
+=head1 OPTIONS
+
+B understands the following options:
+
+=over 4
+
+=item B<-h>
+
+Help, display usage information.
+
+=item B<-H>
+
+Conversion help, display information about converting reference documentation
+from the EPICS Wiki into a B<.dbd.pod> file for use with this tool.
+
+=item B<-D>
+
+Instead of creating the output file as described, read the input file(s) and
+print a B dependency rule for the output file(s) to stdout.
+
+=item B<-o> file
+
+Name of the output file to be created.
+
+=back
+
+If no output filename is set, the file created will be named after the input
+file, removing any directory components in the path and replacing any
+B<.dbd.pod> file extension with B<.html>.
+
+=cut
+
+our ($opt_h, $opt_H, $opt_D, @opt_I, $opt_o);
+
+my $tool = 'dbdToHtml.pl';
+
+getopts('hHDI@o:') or
+ pod2usage(2);
+pod2usage(-verbose => 2) if $opt_H;
+pod2usage(1) if $opt_h;
+pod2usage("$tool: No input file given.\n") if @ARGV != 1;
my $dbd = DBD->new();
my $infile = shift @ARGV;
$infile =~ m/\.dbd.pod$/ or
- die "$tool: Input file '$infile' must have '.dbd.pod' extension\n";
+ pod2usage("$tool: Input file '$infile' must have '.dbd.pod' extension.\n");
ParseDBD($dbd, Readfile($infile, 0, \@opt_I));
@@ -245,3 +295,88 @@ sub DBD::Recfield::writable {
if $special eq "SPC_DBADDR";
return $fld->dbf_type eq "DBF_NOACCESS" ? 'No' : 'Yes';
}
+
+=pod
+
+=head1 Converting Wiki Record Reference to POD
+
+If you open the src/std/rec/aiRecord.dbd.pod file in your favourite plain text
+editor you'll see what input was required to generate the aiRecord.html file.
+The text markup language we're using is a standard called POD (Plain Old
+Documentation) which is used by Perl developers, but you don't need to know Perl
+at all to be able to use it.
+
+When we add POD markup to a record type, we rename its *Record.dbd file to
+.dbd.pod in the src/std/rec directory; no other changes are needed for the build
+system to find it by its new name. The POD content is effectively just a new
+kind of comment that appears in .dbd.pod files, which the formatter knows how to
+convert into HTML. The build also generates a plain *Record.dbd file from this
+same input file by stripping out all of the POD markup.
+
+Documentation for Perl's POD markup standard can be found online at
+L or you may be able to type 'perldoc
+perlpod' into a Linux command-line to see the same text. We added a few POD
+keywords of our own to handle the table generation, and I'll cover those briefly
+below.
+
+POD text can appear almost anywhere in a dbd.pod file. It always starts with a
+line "=[keyword] [additional text...]" where [keyword] is "title", "head1"
+through "head4" etc.. The POD text ends with a line "=cut". There must be a
+blank line above every POD line, and in many cases below it as well.
+
+The POD keywords we have added are "title", "recordtype", "menu", "fields",
+"type", "read" and "write". The last 3 are less common but are used in some of
+the other record types such as the waveform and aSub records.
+
+The most interesting of our new keywords is "fields", which takes a list of
+record field names on the same line after the keyword and generates an HTML
+Table describing those fields based on the field description found in the DBD
+parts. In the ai documentation the first such table covers the DTYP and INP
+fields, so the line
+
+ =fields DTYP, INP
+
+generates all this in the output:
+
+
+
+ | Field | Summary | Type | DCT |
+ Default | Read | Write | CA PP |
+
+
+ | DTYP | Device Type |
+ DEVICE |
+ Yes |
+ |
+ Yes |
+ Yes |
+ No |
+
+
+ | INP |
+ Input Specification |
+ INLINK |
+ Yes |
+ |
+ Yes |
+ Yes |
+ No |
+
+
+
+Note that the "=fields" line must appear inside the DBD's declaration of the
+record type, i.e. after the line
+
+ recordtype(ai) {
+
+The "type", "read" and "write" POD keywords are used inside an individual record
+field declaration and provide information for the "Type", "Read" and "Write"
+columns of the field's table output for fields where this information is
+normally supplied by the record support code. Usage examples for these keywords
+can be found in the aai and aSub record types.
+
+If you look at the L file you'll see that the POD there starts
+by documenting a record-specific menu definition. The "menu" keyword generates a
+table that lists all the choices found in the named menu.
+
+=cut
diff --git a/modules/database/test/ioc/db/dbCaLinkTest.c b/modules/database/test/ioc/db/dbCaLinkTest.c
index 838fde6ce..62be1c0da 100644
--- a/modules/database/test/ioc/db/dbCaLinkTest.c
+++ b/modules/database/test/ioc/db/dbCaLinkTest.c
@@ -29,6 +29,7 @@
/* Declarations from cadef.h and db_access.h which we can't include here */
typedef void * chid;
+typedef void * evid;
epicsShareExtern const unsigned short dbr_value_size[];
epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
#define MAX_UNITS_SIZE 8
diff --git a/modules/libcom/test/epicsCalcTest.cpp b/modules/libcom/test/epicsCalcTest.cpp
index 1ebc0c578..2492c95ba 100644
--- a/modules/libcom/test/epicsCalcTest.cpp
+++ b/modules/libcom/test/epicsCalcTest.cpp
@@ -391,7 +391,7 @@ MAIN(epicsCalcTest)
testExpr(isnan(0.));
testExpr(isnan(Inf));
testExpr(isnan(-Inf));
- testExpr(isnan(NaN));
+ testExpr(!!isnan(NaN)); // As above
testCalc("isnan(0,1,2)", 0);
testCalc("isnan(0,1,NaN)", 1);
testCalc("isnan(0,NaN,2)", 1);
diff --git a/src/tools/Makefile b/src/tools/Makefile
index cef1127bf..d07092271 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -41,6 +41,8 @@ HTMLS += EPICS/Path.html
HTMLS += EPICS/Readfile.html
HTMLS += fullPathName.html
HTMLS += munch.html
+HTMLS += podToHtml.html
+HTMLS += podRemove.html
# Build Package Config Files