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

+ + + + + + + + + + + + + + + + + + + + + + + +
FieldSummaryTypeDCTDefaultReadWriteCA PP
DTYPDevice TypeDEVICEYes YesYesNo
INPInput SpecificationINLINKYes YesYesNo
+ +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