Merge branch '3.15' into epicsEnvUnset

Conflicts:
	documentation/RELEASE_NOTES.html
This commit is contained in:
2018-10-08 11:02:03 +02:00
57 changed files with 1609 additions and 1096 deletions
+4
View File
@@ -140,6 +140,10 @@ sub display {
printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit};
printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit};
}
if (exists $data->{ackt}) {
printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO';
printf " Ack severity: %s\n", $data->{acks};
}
} else {
my $value = format_number($data, $type);
if ($opt_t) {
@@ -644,6 +644,11 @@ caStatus casDGClient::processDG ()
if ( status != S_cas_success ) {
break;
}
if ( this->in.bytesPresent () > 0 && dgInBytesConsumed == 0 && status == S_cas_success ) {
this->in.removeMsg ( this->in.bytesPresent() );
}
}
return status;
}
+20 -9
View File
@@ -649,11 +649,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);
@@ -701,6 +706,7 @@ static void eventCallback(struct event_handler_args arg)
struct dbr_time_double *pdbr_time_double;
dbCaCallback monitor = 0;
void *userPvt = 0;
int doScan = 1;
assert(pca);
epicsMutexMustLock(pca->lock);
@@ -729,10 +735,13 @@ static void eventCallback(struct event_handler_args arg)
memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size);
pca->gotInString = TRUE;
} else switch (arg.type){
case DBR_TIME_ENUM:
/* Disable the record scan if we also have a string monitor */
doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString);
/* fall through */
case DBR_TIME_STRING:
case DBR_TIME_SHORT:
case DBR_TIME_FLOAT:
case DBR_TIME_ENUM:
case DBR_TIME_CHAR:
case DBR_TIME_LONG:
case DBR_TIME_DOUBLE:
@@ -748,7 +757,7 @@ static void eventCallback(struct event_handler_args arg)
pca->sevr = pdbr_time_double->severity;
pca->stat = pdbr_time_double->status;
memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp));
if (precord) {
if (doScan && precord) {
struct pv_link *ppv_link = &plink->value.pv_link;
if ((ppv_link->pvlMask & pvlOptCP) ||
@@ -1005,7 +1014,8 @@ static void dbCaTask(void *arg)
status = ca_add_array_event(
ca_field_type(pca->chid)+DBR_TIME_STRING,
ca_element_count(pca->chid),
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));
@@ -1017,7 +1027,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));
+2
View File
@@ -78,6 +78,8 @@ typedef struct caLink
char *pgetString;
void *pputNative;
char *pputString;
evid evidNative;
evid evidString;
char gotInNative;
char gotInString;
char gotOutNative;
+4
View File
@@ -18,6 +18,7 @@
#include <limits.h>
#include "dbDefs.h"
#include "alarm.h"
#include "epicsMath.h"
#include "epicsPrint.h"
#include "epicsStdlib.h"
@@ -181,6 +182,9 @@ unsigned short recGblResetAlarms(void *precord)
epicsEnum16 val_mask = 0;
epicsEnum16 stat_mask = 0;
if (new_sevr > INVALID_ALARM)
new_sevr = INVALID_ALARM;
pdbc->stat = new_stat;
pdbc->sevr = new_sevr;
pdbc->nsta = 0;
+547 -304
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -34,10 +34,10 @@ be written to stdout unless the -o option is given.</p>
<dl>
<dt><tt>-V</tt></dt>
<dd>Verbose warnings; if this parameter is specified then any undefined
macro discovered in the template file which does not have an associated
default value is considered an error. An error message is generated, and
when msi terminates it will do so with an exit status of 2.</dd>
<dd>Verbose warnings; if this parameter is specified then any undefined or
recursive macros discovered in the template will be considered an error and
will be marked in the output file. An error message will be shown, and when
msi terminates it will do so with an exit status of 2.</dd>
<dt><tt>-g</tt></dt>
<dd>When this flag is given all macros defined in a substitution file will
+3 -3
View File
@@ -31,18 +31,18 @@ ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt'))
# Substitution file, pattern format
ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
# Output option -o
# Output option -o and verbose option -V
my $out = 't7-output.txt';
my $count = 5; # Try up to 5 times...
my $result;
do {
unlink $out;
msi("-I.. -o $out ../t1-template.txt");
msi("-I.. -V -o $out ../t1-template.txt");
$result = slurp($out);
print "# msi output file empty, retrying\n"
if $result eq '';
} while ($result eq '') && (--$count > 0);
ok($result, slurp('../t1-result.txt'));
ok($result, slurp('../t7-result.txt'));
# Dependency generation, include/substitute model
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
+1 -1
View File
@@ -1,6 +1,6 @@
This is t1-template.txt
With $(a,undefined) & $(b,undefined):
With $(a) & $(b):
This is t1-include.txt
a = default value used when a is undefined
b = default value used when b is undefined
+6 -6
View File
@@ -1,6 +1,6 @@
a = va1-a b = def-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = def-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = va1-a b = def-b c = def-c d = $(d)
a = va2-a b = va2-b c = def-c d = $(d)
a = va3-a b = va3-b c = va3-c d = $(d)
a = va4-a b = va4-b c = def-c d = $(d)
a = va5-a b = def-b c = def-c d = $(d)
a = pt3-a b = pt3-b c = pt3-c d = $(d)
+28 -28
View File
@@ -1,28 +1,28 @@
a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
a = va1-a b = gb1-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = gb1-b c = def-c d = $(d,undefined)
a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
a = va1-a b = gb2-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = gb2-b c = def-c d = $(d,undefined)
a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
a = pt1-a b = gb3-b c = def-c d = $(d,undefined)
a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
a = pt5-a b = gb3-b c = def-c d = $(d,undefined)
a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
a = pt1-a b = gb4-b c = def-c d = $(d,undefined)
a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
a = pt5-a b = gb4-b c = def-c d = $(d,undefined)
a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
a = gb1-a b = gb1-b c = def-c d = $(d)
a = va1-a b = gb1-b c = def-c d = $(d)
a = va2-a b = va2-b c = def-c d = $(d)
a = va3-a b = va3-b c = va3-c d = $(d)
a = va4-a b = va4-b c = def-c d = $(d)
a = va5-a b = gb1-b c = def-c d = $(d)
a = gb1-a b = gb1-b c = def-c d = $(d)
a = gb2-a b = gb2-b c = def-c d = $(d)
a = va1-a b = gb2-b c = def-c d = $(d)
a = va2-a b = va2-b c = def-c d = $(d)
a = va3-a b = va3-b c = va3-c d = $(d)
a = va4-a b = va4-b c = def-c d = $(d)
a = va5-a b = gb2-b c = def-c d = $(d)
a = gb2-a b = gb2-b c = def-c d = $(d)
a = gb3-a b = gb3-b c = def-c d = $(d)
a = pt1-a b = gb3-b c = def-c d = $(d)
a = pt2-a b = pt2-b c = def-c d = $(d)
a = pt3-a b = pt3-b c = pt3-c d = $(d)
a = pt4-a b = pt4-b c = def-c d = $(d)
a = pt5-a b = gb3-b c = def-c d = $(d)
a = gb3-a b = gb3-b c = def-c d = $(d)
a = gb4-a b = gb4-b c = def-c d = $(d)
a = pt1-a b = gb4-b c = def-c d = $(d)
a = pt2-a b = pt2-b c = def-c d = $(d)
a = pt3-a b = pt3-b c = pt3-c d = $(d)
a = pt4-a b = pt4-b c = def-c d = $(d)
a = pt5-a b = gb4-b c = def-c d = $(d)
a = gb4-a b = gb4-b c = def-c d = $(d)
+6 -6
View File
@@ -1,6 +1,6 @@
a = va1-a b = def-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = va3-c d = $(d,undefined)
a = va5-a b = va4-b c = va3-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = va1-a b = def-b c = def-c d = $(d)
a = va2-a b = va2-b c = def-c d = $(d)
a = va3-a b = va3-b c = va3-c d = $(d)
a = va4-a b = va4-b c = va3-c d = $(d)
a = va5-a b = va4-b c = va3-c d = $(d)
a = pt3-a b = pt3-b c = pt3-c d = $(d)
+4 -4
View File
@@ -2,19 +2,19 @@
a = 111
b = 222
c = xx
d = $(d,undefined)
d = $(d)
# comment line
a = aaa
b = bbb
c = ccc
d = $(d,undefined)
d = $(d)
# comment line
a = AA
b = BB
c = xx
d = $(d,undefined)
d = $(d)
# comment line
a = aaa
b = bbb
c = yy
d = $(d,undefined)
d = $(d)
+4 -4
View File
@@ -2,19 +2,19 @@
a = 111
b = 222
c = xx
d = $(d,undefined)
d = $(d)
# comment line
a = aaa
b = bbb
c = ccc
d = $(d,undefined)
d = $(d)
# comment line
a = AA
b = BB
c = xx
d = $(d,undefined)
d = $(d)
# comment line
a = aaa
b = bbb
c = yy
d = $(d,undefined)
d = $(d)
+21
View File
@@ -0,0 +1,21 @@
This is t1-template.txt
With $(a,undefined) & $(b,undefined):
This is t1-include.txt
a = default value used when a is undefined
b = default value used when b is undefined
End of t1-include.txt
On defining a=aaa & b=bbb:
This is t1-include.txt again
a = aaa
b = bbb
End of t1-include.txt
On setting a="aa":
This is t1-include.txt again
a = "aa"
b = bbb
End of t1-include.txt
End of t1-template.txt
+1
View File
@@ -26,3 +26,4 @@ dbCore_SRCS += miscIocRegister.c
dbCore_SRCS += dlload.c
dbCore_SRCS += iocshRegisterCommon.c
miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE
+7 -2
View File
@@ -66,10 +66,12 @@ void miscIocRegister(void)
/* system -- escape to system command interpreter.
*
* Disabled by default, for security reasons. To enable this command, add
* Disabled by default for security reasons, not available on all OSs.
* To enable this command, add
* registrar(iocshSystemCommand)
* to an application dbd file.
* to an application dbd file, or include system.dbd
*/
#ifndef SYSTEM_UNAVAILABLE
static const iocshArg systemArg0 = { "command string",iocshArgString};
static const iocshArg * const systemArgs[] = {&systemArg0};
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs};
@@ -77,12 +79,15 @@ static void systemCallFunc(const iocshArgBuf *args)
{
system(args[0].sval);
}
#endif
static void iocshSystemCommand(void)
{
#ifndef SYSTEM_UNAVAILABLE
if (system(NULL))
iocshRegister(&systemFuncDef, systemCallFunc);
else
#endif
errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n");
}
epicsExportRegistrar(iocshSystemCommand);
+9 -2
View File
@@ -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.
\*************************************************************************/
/* flex - tool to generate fast lexical analyzers */
@@ -14,7 +14,7 @@
*
* This code is derived from software contributed to Berkeley by
* Vern Paxson.
*
*
* The United States Government has rights in this work pursuant
* to contract no. DE-AC03-76SF00098 between the United States
* Department of Energy and the University of California.
@@ -495,6 +495,13 @@ void flexinit(int argc, char **argv)
/* stupid do-nothing deprecated option */
break;
case 'o':
if ( i != 1 )
flexerror( "-o flag must be given separately" );
outfile = arg + i + 1;
goto get_next_arg;
case 'p':
performance_report = true;
break;
+2 -7
View File
@@ -347,11 +347,6 @@ static int openLogFile (struct ioc_log_server *pserver)
{
enum TF_RETURN ret;
if (ioc_log_file_limit==0u) {
pserver->poutfile = stderr;
return IOCLS_ERROR;
}
if (pserver->poutfile && pserver->poutfile != stderr){
fclose (pserver->poutfile);
pserver->poutfile = NULL;
@@ -627,7 +622,7 @@ static void writeMessagesToLog (struct iocLogClient *pclient)
strlen(pclient->ascii_time) + nchar + 3u;
assert (nTotChar <= INT_MAX);
ntci = (int) nTotChar;
if ( pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) {
if ( pclient->pserver->max_file_size && pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) {
if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) {
unsigned nPadChar;
/*
@@ -771,7 +766,7 @@ static int getConfig(void)
&EPICS_IOC_LOG_FILE_LIMIT,
&ioc_log_file_limit);
if(status>=0){
if (ioc_log_file_limit<=0) {
if (ioc_log_file_limit < 0) {
envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT);
return IOCLS_ERROR;
}
+7 -2
View File
@@ -902,11 +902,16 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
}
}
/* Bad reference, insert $(name,errval) */
/* Bad reference, insert either $(name,<error>) or $(name) */
if ( v < valend ) *v++ = '$';
if ( v < valend ) *v++ = '(';
cpy2val( refname, &v, valend );
cpy2val( errval, &v, valend );
if (handle->flags & FLAG_SUPPRESS_WARNINGS) {
if ( v < valend ) *v++ = ')';
*v = '\0';
}
else
cpy2val( errval, &v, valend );
cleanup:
if (pop) {
+26 -13
View File
@@ -34,20 +34,22 @@ extern "C" {
/* Make printf, puts and putchar use *our* version of stdout */
#ifdef printf
# undef printf
#endif /* printf */
#define printf epicsStdoutPrintf
#ifndef epicsStdioStdPrintfEtc
# ifdef printf
# undef printf
# endif
# define printf epicsStdoutPrintf
#ifdef puts
# undef puts
#endif /* puts */
#define puts epicsStdoutPuts
# ifdef puts
# undef puts
# endif
# define puts epicsStdoutPuts
#ifdef putchar
# undef putchar
#endif /* putchar */
#define putchar epicsStdoutPutchar
# ifdef putchar
# undef putchar
# endif
# define putchar epicsStdoutPutchar
#endif
epicsShareFunc int epicsShareAPI epicsSnprintf(
char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4);
@@ -87,6 +89,17 @@ epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c);
#ifdef __cplusplus
}
#endif
/* Also pull functions into the std namespace (see lp:1786927) */
namespace std {
using ::epicsGetStdin;
using ::epicsGetStdout;
using ::epicsGetStderr;
using ::epicsStdoutPrintf;
using ::epicsStdoutPuts;
using ::epicsStdoutPutchar;
}
#endif /* __cplusplus */
#endif /* epicsStdioh */
+7 -16
View File
@@ -19,13 +19,13 @@
#endif
#include <stdlib.h>
#include <stdio.h>
#define STRICT
#include <windows.h>
#define epicsExportSharedSymbols
#include "osiProcess.h"
#include "errlog.h"
epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn)
{
@@ -94,16 +94,11 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce
pFmtArgs[1] = (char *) pBaseExecutableName;
pFmtArgs[2] = errStrMsgBuf;
pFmtArgs[3] = "Changes may be required in your \"path\" environment variable.";
pFmtArgs[4] = "PATH = ";
pFmtArgs[5] = getenv ("path");
if ( pFmtArgs[5] == NULL ) {
pFmtArgs[5] = "<empty string>";
}
W32status = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY | 80,
"%1 \"%2\". %3 %4 %5 \"%6\"",
"%1 \"%2\". %3 %4",
0,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
(LPTSTR) &complteMsgBuf,
@@ -111,24 +106,20 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce
pFmtArgs
);
if (W32status) {
/* Display the string. */
MessageBox (NULL, complteMsgBuf, "Configuration Problem",
MB_OK | MB_ICONINFORMATION);
fprintf (stderr, "%s\n", (char *) complteMsgBuf);
LocalFree (complteMsgBuf);
}
else {
/* Display the string. */
MessageBox (NULL, errStrMsgBuf, "Failed to start executable",
MB_OK | MB_ICONINFORMATION);
fprintf (stderr, "%s\n", (char *) errStrMsgBuf);
}
/* Free the buffer. */
LocalFree (errStrMsgBuf);
}
else {
errlogPrintf ("!!WARNING!!\n");
errlogPrintf ("Unable to locate executable \"%s\".\n", pBaseExecutableName);
errlogPrintf ("You may need to modify your \"path\" environment variable.\n");
fprintf (stderr, "!!WARNING!!\n");
fprintf (stderr, "Unable to locate executable \"%s\".\n", pBaseExecutableName);
fprintf (stderr, "You may need to modify your \"path\" environment variable.\n");
}
return osiSpawnDetachedProcessFail;
}
+21 -4
View File
@@ -41,16 +41,18 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
#ifdef CLOCK_REALTIME
/* This code is not used on systems without Posix CLOCK_REALTIME such
* as Darwin, but the only way to detect that is from the OS headers,
* so the Makefile can't exclude building this file on those systems.
/* This code is not used on systems without Posix CLOCK_REALTIME,
* but the only way to detect that is from the OS headers, so the
* Makefile can't exclude compiling this file on those systems.
*/
/* Forward references */
static int ClockTimeGetCurrent(epicsTimeStamp *pDest);
static void ClockTimeSync(void *dummy);
#if defined(vxWorks) || defined(__rtems__)
static void ClockTimeSync(void *dummy);
#endif
/* ClockTime_Report iocsh command */
static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv};
@@ -98,12 +100,18 @@ void ClockTime_Init(int synchronize)
if (synchronize == CLOCKTIME_SYNC) {
if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) {
#if defined(vxWorks) || defined(__rtems__)
/* Start synchronizing */
ClockTimePvt.synchronize = synchronize;
epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
epicsThreadGetStackSize(epicsThreadStackSmall),
ClockTimeSync, NULL);
#else
errlogPrintf("Clock synchronization must be performed by the OS\n");
#endif
}
else {
/* No change, sync thread should already be running */
@@ -139,6 +147,7 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest)
/* Synchronization thread */
#if defined(vxWorks) || defined(__rtems__)
static void ClockTimeSync(void *dummy)
{
taskwdInsert(0, NULL, NULL);
@@ -177,6 +186,7 @@ static void ClockTimeSync(void *dummy)
ClockTimePvt.synchronized = 0;
taskwdRemove(0);
}
#endif
/* Time Provider Routine */
@@ -188,6 +198,7 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
/* If a Hi-Res clock is available and works, use it */
#ifdef CLOCK_REALTIME_HR
clock_gettime(CLOCK_REALTIME_HR, &clockNow) &&
/* Note: Uses the lo-res clock below if the above call fails */
#endif
clock_gettime(CLOCK_REALTIME, &clockNow);
@@ -195,9 +206,15 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) {
clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400;
clockNow.tv_nsec = 0;
#if defined(vxWorks) || defined(__rtems__)
clock_settime(CLOCK_REALTIME, &clockNow);
errlogPrintf("WARNING: OS Clock time was read before being set.\n"
"Using 1990-01-02 00:00:00.000000 UTC\n");
#else
errlogPrintf("WARNING: OS Clock pre-dates the EPICS epoch!\n"
"Using 1990-01-02 00:00:00.000000 UTC\n");
#endif
}
epicsTimeFromTimespec(pDest, &clockNow);
+1 -1
View File
@@ -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);
+6 -2
View File
@@ -65,11 +65,11 @@ static void ovcheck(void)
MAIN(macLibTest)
{
testPlan(91);
testPlan(93);
if (macCreateHandle(&h, NULL))
testAbort("macCreateHandle() failed");
macSuppressWarning(h, TRUE);
eltc(0);
check("FOO", " FOO");
@@ -215,6 +215,10 @@ MAIN(macLibTest)
check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)");
check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)");
macSuppressWarning(h, TRUE);
check("$(CRUX)", "!$(CRUX)");
check("${FOO}", "!$(BAR)");
ovcheck();
return testDone();
+8 -2
View File
@@ -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.
\*************************************************************************/
/*
@@ -22,6 +22,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "cantProceed.h"
@@ -71,13 +72,18 @@ static long init_record(aaiRecord *prec)
static long read_aai(aaiRecord *prec)
{
epicsUInt32 nord = prec->nord;
long nRequest = prec->nelm;
dbGetLink(prec->simm == menuYesNoYES ? &prec->siol : &prec->inp,
prec->ftvl, prec->bptr, 0, &nRequest);
if (nRequest > 0) {
prec->nord = nRequest;
prec->udf=FALSE;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
prec->udf = FALSE;
if (prec->tsel.type == CONSTANT &&
prec->tse == epicsTimeEventDeviceTime)
dbGetTimeStamp(&prec->inp, &prec->time);
+5 -1
View File
@@ -4,7 +4,7 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* 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.
\*************************************************************************/
/*
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "subArrayRecord.h"
@@ -67,6 +68,7 @@ static long init_record(subArrayRecord *prec)
static long read_sa(subArrayRecord *prec)
{
long nRequest = prec->indx + prec->nelm;
epicsUInt32 nord = prec->nord;
long ecount;
if (nRequest > prec->malm)
@@ -89,6 +91,8 @@ static long read_sa(subArrayRecord *prec)
ecount = 0;
prec->nord = ecount;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (nRequest > 0 &&
prec->tsel.type == CONSTANT &&
+6 -1
View File
@@ -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.
\*************************************************************************/
/*
@@ -67,11 +67,16 @@ static long init_record(waveformRecord *prec)
static long read_wf(waveformRecord *prec)
{
epicsUInt32 nord = prec->nord;
long nRequest = prec->nelm;
dbGetLink(&prec->inp, prec->ftvl, prec->bptr, 0, &nRequest);
if (nRequest > 0) {
prec->nord = nRequest;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (prec->tsel.type == CONSTANT &&
prec->tse == epicsTimeEventDeviceTime)
dbGetTimeStamp(&prec->inp, &prec->time);
+24 -12
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* 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.
\*************************************************************************/
/* recAai.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
*
*
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -139,12 +139,12 @@ static long init_record(aaiRecord *prec, int pass)
}
return 0;
}
/* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */
if (prec->siml.type == CONSTANT) {
recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
}
/* must have read_aai function defined */
if (pdset->number < 5 || pdset->read_aai == NULL) {
recGblRecordError(S_dev_missingSup, prec, "aai: init_record");
@@ -204,10 +204,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -220,7 +224,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -314,12 +318,11 @@ static void monitor(aaiRecord *prec)
static long readValue(aaiRecord *prec)
{
long status;
struct aaidset *pdset = (struct aaidset *)prec->dset;
long status;
if (prec->pact == TRUE){
status = pdset->read_aai(prec);
return status;
return pdset->read_aai(prec);
}
status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0);
@@ -327,10 +330,16 @@ static long readValue(aaiRecord *prec)
return status;
if (prec->simm == menuYesNoNO){
return pdset->read_aai(prec);
epicsUInt32 nord = prec->nord;
status = pdset->read_aai(prec);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return status;
}
if (prec->simm == menuYesNoYES){
epicsUInt32 nord = prec->nord;
/* Device suport is responsible for buffer
which might be read-only so we may not be
allowed to call dbGetLink on it.
@@ -339,10 +348,13 @@ static long readValue(aaiRecord *prec)
Thus call device now.
*/
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
return pdset->read_aai(prec);
status = pdset->read_aai(prec);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return status;
}
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
return -1;
}
+7 -4
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* 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.
\*************************************************************************/
/* recAao.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
*
*
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -204,10 +204,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -220,7 +224,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -343,4 +347,3 @@ static long writeValue(aaoRecord *prec)
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
return -1;
}
+6 -2
View File
@@ -4,12 +4,12 @@
* 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.
\*************************************************************************/
/*
* Original Author: Bob Dalesio
* Date: 7-14-89
* Date: 7-14-89
*/
#include <stddef.h>
@@ -405,11 +405,15 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
compressRecord *prec = (compressRecord *) paddr->precord;
epicsUInt32 nuse = prec->nuse;
prec->off = (prec->off + nNew) % prec->nsam;
prec->nuse += nNew;
if (prec->nuse > prec->nsam)
prec->nuse = prec->nsam;
if (nuse != prec->nuse)
db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG);
return 0;
}
+10 -8
View File
@@ -2,17 +2,17 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* 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.
\*************************************************************************/
/* recSubArray.c - Record Support Routines for SubArray records
/* recSubArray.c - Record Support Routines for SubArray records
*
*
* Author: Carl Lionberger
* Date: 090293
*
* NOTES:
* Derived from waveform record.
* Derived from waveform record.
* Modification Log:
* -----------------
*/
@@ -124,7 +124,7 @@ static long init_record(subArrayRecord *prec, int pass)
}
if (pdset->init_record)
return (*pdset->init_record)(prec);
return pdset->init_record(prec);
return 0;
}
@@ -191,11 +191,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
subArrayRecord *prec = (subArrayRecord *) paddr->precord;
epicsUInt32 nord = prec->nord;
if (nNew > prec->malm)
nNew = prec->malm;
prec->nord = nNew;
if (prec->nord > prec->malm)
prec->nord = prec->malm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -208,7 +211,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -318,4 +321,3 @@ static long readValue(subArrayRecord *prec)
return status;
}
+14 -12
View File
@@ -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.
\*************************************************************************/
/* recWaveform.c - Record Support Routines for Waveform records */
@@ -184,12 +184,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
waveformRecord *prec = (waveformRecord *) paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -202,7 +204,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -305,36 +307,37 @@ static void monitor(waveformRecord *prec)
static long readValue(waveformRecord *prec)
{
long status;
struct wfdset *pdset = (struct wfdset *) prec->dset;
long status;
if (prec->pact == TRUE){
return (*pdset->read_wf)(prec);
return pdset->read_wf(prec);
}
status = dbGetLink(&(prec->siml), DBR_ENUM, &(prec->simm),0,0);
status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0);
if (status)
return status;
if (prec->simm == menuYesNoNO){
epicsUInt32 nord = prec->nord;
status = (*pdset->read_wf)(prec);
status = pdset->read_wf(prec);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return status;
}
if (prec->simm == menuYesNoYES){
epicsUInt32 nord = prec->nord;
long nRequest = prec->nelm;
status = dbGetLink(&(prec->siol), prec->ftvl, prec->bptr, 0, &nRequest);
/* nord set only for db links: needed for old db_access */
status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest);
if (prec->siol.type != CONSTANT) {
prec->nord = nRequest;
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (status == 0)
prec->udf=FALSE;
prec->udf = FALSE;
}
} else {
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
@@ -344,4 +347,3 @@ static long readValue(waveformRecord *prec)
return status;
}
+1 -2
View File
@@ -1,5 +1,4 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell;
#!/usr/bin/env perl
#*************************************************************************
# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env perl
#*************************************************************************
# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
# Returns an architecture name for EPICS_HOST_ARCH that should be
# appropriate for the CPU that this version of Perl was built for.
# Any arguments to the program will be appended with separator '-'
# to allow flags like -gnu -debug and/or -static to be added.
# Before Base has been built, use a command like this:
# bash$ export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl`
#
# If Base is already built, use
# tcsh% setenv EPICS_HOST_ARCH `perl base/lib/perl/EpicsHostArch.pl`
# If your architecture is not recognized by this script, please send
# the output from running 'perl --version' to the EPICS tech-talk
# mailing list to have it added.
use strict;
use Config;
use POSIX;
print join('-', HostArch(), @ARGV), "\n";
sub HostArch {
my $arch = $Config{archname};
for ($arch) {
return 'linux-x86_64' if m/^x86_64-linux/;
return 'linux-x86' if m/^i[3-6]86-linux/;
return 'linux-arm' if m/^arm-linux/;
return 'windows-x64' if m/^MSWin32-x64/;
return 'win32-x86' if m/^MSWin32-x86/;
return "cygwin-x86_64" if m/^x86_64-cygwin/;
return "cygwin-x86" if m/^i[3-6]86-cygwin/;
return 'solaris-sparc' if m/^sun4-solaris/;
return 'solaris-x86' if m/^i86pc-solaris/;
my ($kernel, $hostname, $release, $version, $cpu) = uname;
if (m/^darwin/) {
for ($cpu) {
return 'darwin-x86' if m/^(i386|x86_64)/;
return 'darwin-ppc' if m/Power Macintosh/;
}
die "$0: macOS CPU type '$cpu' not recognized\n";
}
die "$0: Architecture '$arch' not recognized\n";
}
}
+3
View File
@@ -32,6 +32,9 @@ PERL_MODULES += DBD/Recordtype.pm
PERL_MODULES += DBD/Registrar.pm
PERL_MODULES += DBD/Variable.pm
# This goes into lib/perl, not bin/<host>
PERL_MODULES += EpicsHostArch.pl
PERL_SCRIPTS += assembleSnippets.pl
PERL_SCRIPTS += convertRelease.pl
PERL_SCRIPTS += cvsclean.pl
+140 -6
View File
@@ -41,19 +41,69 @@ 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<dbdToHtml.pl> [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<dbdToHtml.pl> 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<Makefile> 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));
ParseDBD($dbd, Readfile($infile, 0, \@opt_I));
if (!$opt_o) {
($opt_o = $infile) =~ s/\.dbd\.pod$/.html/;
@@ -243,3 +293,87 @@ sub DBD::Recfield::writable {
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<https://perldoc.perl.org/perlpod.html> 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:
<blockquote><table border="1">
<tr>
<th>Field</th><th>Summary</th><th>Type</th><th>DCT</th>
<th>Default</th><th>Read</th><th>Write</th><th>CA PP</th>
</tr>
<tr>
<td class="cell">DTYP</td><td class="cell">Device Type</td>
<td class="cell">DEVICE</td>
<td class="cell">Yes</td>
<td class="cell">&nbsp;</td>
<td class="cell">Yes</td>
<td class="cell">Yes</td>
<td class="cell">No</td>
</tr>
<tr>
<td class="cell">INP</td>
<td class="cell">Input Specification</td>
<td class="cell">INLINK</td>
<td class="cell">Yes</td>
<td class="cell">&nbsp;</td>
<td class="cell">Yes</td>
<td class="cell">Yes</td>
<td class="cell">No</td>
</tr>
</table></blockquote>
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<aoRecord.dbd.pod> 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