Compare commits

..

13 Commits

12 changed files with 135 additions and 84 deletions

View File

@ -44,7 +44,7 @@ ifneq (${EPICS_BASETYPE},3.13)
RECORDTYPES += calcout
endif
StreamCore.o: streamReferences
StreamCore.o StreamCore.d: streamReferences
streamReferences:
perl ../src/makeref.pl Interface $(BUSSES) > $@

View File

@ -564,8 +564,8 @@ in architecture specific RELEASE.Common.<em>arch</em> files.
If the regular expression is not anchored, i.e. does not start with
<code>^</code>, leading non-matching input is skipped.
A maximum of <em>width</em> bytes is matched, if specified.
If <em>precision</em> is given, it specifies the sub-expression whose match
is retuned.
If <em>precision</em> is given, it specifies the sub-expression in <code>()</code>
whose match is retuned.
Otherwise the complete match is returned.
In any case, the complete match is consumed from the input buffer.
If the expression contains a <code>/</code> it must be escaped like <code>\/</code>.
@ -586,12 +586,19 @@ as a post-processor for output.
</p>
<p>
Matches of the <em>regex</em> are replaced by the string <em>subst</em> with all
<code>&</code> or <code>\0</code> in <em>subst</em> replaced with the match itself and all
<code>\1</code> through <code>\9</code> replaced with the match of the corresponding sub-expression.
<code>&</code> in <em>subst</em> replaced with the match itself and all
<code>\1</code> through <code>\9</code> replaced with the match of the corresponding
sub-expression <span class="new"> if such a sub-expression exists.
Due to limitations of the parser, <code>\1</code> and <code>\x01</code> are the same
which makes it difficult to use literal bytes with values lower than 10 in <em>subst</em>.
Therefore <code>\0</code> aways means a literal byte (incompatible change from earlier version!)
and <code>\1</code> through <code>\9</code> mean literal bytes if they are larger than
the number of sub-expressions.
</span>
To get a literal <code>&</code> or <code>\</code> or <code>/</code> in the substitution write
<code>\&</code> or <code>\\</code> or <code>\/</code>.
There is no way to specify literal bytes with values less or equal to 9 in the
substitution!
</p>
<p>
If <em>width</em> is specified, it limits the number of characters processed.

View File

@ -562,13 +562,7 @@ connectToAsynPort()
clientName(), connected ? "already" : "not yet");
if (!connected)
{
printf ("%s: AsynDriverInterface::connectToAsynPort: "
"pasynCommon->connect(%p, %p)\n",
clientName(), pvtCommon, pasynUser);
status = pasynCommon->connect(pvtCommon, pasynUser);
printf ("%s: AsynDriverInterface::connectToAsynPort: "
"pasynCommon->connect(%p, %p) = %s\n",
clientName(), pvtCommon, pasynUser, asynStatusStr[status]);
debug("AsynDriverInterface::connectToAsynPort(%s): "
"status=%s\n",
clientName(), asynStatusStr[status]);
@ -718,13 +712,19 @@ writeHandler()
pasynOctet->setOutputEos(pvtOctet, pasynUser,
NULL, 0);
}
pasynUser->errorMessage[0] = 0;
status = pasynOctet->write(pvtOctet, pasynUser,
outputBuffer, outputSize, &written);
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::writeHandler(%s): "
"write(..., outputSize=%ld, written=%ld) "
"[timeout=%g sec] = %s\n",
clientName(), (long)outputSize, (long)written,
pasynUser->timeout, asynStatusStr[status]);
"write(..., \"%s\", outputSize=%ld, written=%ld) "
"[timeout=%g sec] = %s (%s)\n",
clientName(),
StreamBuffer(outputBuffer, outputSize).expand()(),
(long)outputSize, (long)written,
pasynUser->timeout, asynStatusStr[status],
pasynUser->errorMessage);
#endif
if (oldeoslen >= 0) // restore asyn terminator
{
@ -769,34 +769,34 @@ writeHandler()
writeCallback(StreamIoSuccess);
return;
case asynTimeout:
error("%s: asynTimeout (%g sec) in write. Asyn says: %s\n",
error("%s: asynTimeout (%g sec) in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->timeout, pasynUser->errorMessage);
writeCallback(StreamIoTimeout);
return;
case asynOverflow:
error("%s: asynOverflow in write. Asyn driver says: %s\n",
error("%s: asynOverflow in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
case asynError:
error("%s: asynError in write. Asyn driver says: %s\n",
error("%s: asynError in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
#ifdef ASYN_VERSION // asyn >= 4.14
case asynDisconnected:
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
case asynDisabled:
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
#endif
default:
error("%s: unknown asyn error in write. Asyn driver says: %s\n",
error("%s: unknown asyn error in write. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
@ -887,6 +887,11 @@ readHandler()
oldeoslen = -1;
} else do {
// device (e.g. GPIB) might not accept full eos length
if ((int)deveoslen == oldeoslen && strcmp(deveos, oldeos) == 0)
{
// nothing to do: old and new eos are the same
break;
}
if (pasynOctet->setInputEos(pvtOctet, pasynUser,
deveos, deveoslen) == asynSuccess)
{
@ -894,8 +899,9 @@ readHandler()
if (ioAction != AsyncRead)
{
debug("AsynDriverInterface::readHandler(%s) "
"input EOS set to %s\n",
"input EOS changed from \"%s\" to \"%s\"\n",
clientName(),
StreamBuffer(oldeos, oldeoslen).expand()(),
StreamBuffer(deveos, deveoslen).expand()());
}
#endif
@ -951,6 +957,7 @@ readHandler()
readMore = 0;
received = 0;
eomReason = 0;
pasynUser->errorMessage[0] = 0;
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
"read(..., bytesToRead=%ld, ...) "
@ -959,12 +966,13 @@ readHandler()
bytesToRead, pasynUser->timeout);
status = pasynOctet->read(pvtOctet, pasynUser,
buffer, bytesToRead, &received, &eomReason);
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::readHandler(%s): "
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
clientName(), asynStatusStr[status], ioActionStr[ioAction],
(long)received,eomReasonStr[eomReason&0x7],
StreamBuffer(buffer, received).expand()());
#endif
pasynManager->isConnected(pasynUser, &connected);
debug("AsynDriverInterface::readHandler(%s): "
"device is now %sconnected\n",
@ -1094,29 +1102,29 @@ readHandler()
}
peeksize = inputBuffer.capacity();
// deliver whatever we could save
error("%s: asynOverflow in read. Asyn driver says: %s\n",
error("%s: asynOverflow in read. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
readCallback(StreamIoFault, buffer, received);
break;
case asynError:
error("%s: asynError in read. Asyn driver says: %s\n",
error("%s: asynError in read. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
readCallback(StreamIoFault, buffer, received);
break;
#ifdef ASYN_VERSION // asyn >= 4.14
case asynDisconnected:
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
readCallback(StreamIoFault);
return;
case asynDisabled:
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
readCallback(StreamIoFault);
return;
#endif
default:
error("%s: unknown asyn error in read. Asyn driver says: %s\n",
error("%s: unknown asyn error in read. Asyn driver says: \"%s\"\n",
clientName(), pasynUser->errorMessage);
readCallback(StreamIoFault);
return;
@ -1138,10 +1146,16 @@ readHandler()
}
// restore original EOS
if (oldeoslen >= 0)
if (oldeoslen >= 0 && oldeoslen != (int)deveoslen && strcmp(deveos, oldeos) != 0)
{
pasynOctet->setInputEos(pvtOctet, pasynUser,
oldeos, oldeoslen);
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::readHandler(%s) "
"input EOS restored to \"%s\"\n",
clientName(),
StreamBuffer(oldeos, oldeoslen).expand()());
#endif
}
}

View File

@ -32,7 +32,6 @@
run-time leak.
- A maximum of 9 subexpressions is supported. Only one of them can
be the result of the match.
- vxWorks and maybe other OS don't have a PCRE library. Provide one?
*/
class RegexpConverter : public StreamFormatConverter
@ -54,9 +53,9 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
}
if (fmt.prec > 9)
{
error("Subexpression index %d too big (>9)\n", fmt.prec);
error("Sub-expression index %d too big (>9)\n", fmt.prec);
return false;
}
}
StreamBuffer pattern;
while (*source != '/')
@ -81,22 +80,30 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
}
source++;
debug("regexp = \"%s\"\n", pattern.expand()());
const char* errormsg;
int eoffset;
pcre* code = pcre_compile(pattern(), 0,
&errormsg, &eoffset, NULL);
int nsubexpr;
pcre* code = pcre_compile(pattern(), 0, &errormsg, &eoffset, NULL);
if (!code)
{
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
return false;
}
pcre_fullinfo(code, NULL, PCRE_INFO_CAPTURECOUNT, &nsubexpr);
if (fmt.prec > nsubexpr)
{
error("Sub-expression index is %d but pattern has only %d sub-expression\n", fmt.prec, nsubexpr);
return false;
}
info.append(&code, sizeof(code));
if (fmt.flags & alt_flag)
{
StreamBuffer subst;
debug("check for subst in \"%s\"\n", StreamBuffer(source).expand()());
debug("check for subst in \"%s\"\n", StreamBuffer(source).expand()());
while (*source != '/')
{
if (!*source) {
@ -122,15 +129,15 @@ scanString(const StreamFormat& fmt, const char* input,
int ovector[30];
int rc;
unsigned int l;
const char* info = fmt.info;
pcre* code = extract<pcre*>(info);
int length = fmt.width > 0 ? fmt.width : strlen(input);
int subexpr = fmt.prec > 0 ? fmt.prec : 0;
debug("input = \"%s\"\n", input);
debug("length=%d\n", length);
rc = pcre_exec(code, NULL, input, length, 0, 0, ovector, 30);
debug("pcre_exec match \"%.*s\" result = %d\n", length, input, rc);
if ((subexpr && rc <= subexpr) || rc < 0)
@ -152,7 +159,7 @@ scanString(const StreamFormat& fmt, const char* input,
}
memcpy(value, input + ovector[subexpr*2], l);
value[l] = '\0';
return ovector[1]; // consume input until end of match
return ovector[1]; // consume input until end of match
}
static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
@ -167,19 +174,19 @@ static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
length = buffer.length() - start;
if (fmt.width && fmt.width < length)
length = fmt.width;
if (fmt.flags & sign_flag)
if (fmt.flags & left_flag)
start = buffer.length() - length;
debug("regsubst buffer=\"%s\", start=%ld, length=%ld, subst = \"%s\"\n",
buffer.expand()(), start, length, subst);
buffer.expand()(), start, length, StreamBuffer(subst).expand()());
for (c = 0, n = 1; c < length; n++)
{
rc = pcre_exec(code, NULL, buffer(start+c), length-c, 0, 0, ovector, 30);
debug("pcre_exec match \"%.*s\" result = %d\n", (int)length-c, buffer(start+c), rc);
if (rc < 0) // no match
debug("pcre_exec match \"%s\" result = %d\n", buffer.expand(start+c, length-c)(), rc);
if (rc < 0) // no match
return;
if (!(fmt.flags & sign_flag) && n < fmt.prec) // without + flag
{
// do not yet replace this match
@ -188,24 +195,24 @@ static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
}
// replace & by match in subst
l = ovector[1] - ovector[0];
debug("start = \"%s\"\n", buffer(start+c));
debug("match = \"%.*s\"\n", l, buffer(start+c+ovector[0]));
debug("before [%d]= \"%s\"\n", ovector[0], buffer.expand(start+c,ovector[0])());
debug("match [%d]= \"%s\"\n", l, buffer.expand(start+c+ovector[0],l)());
for (r = 1; r < rc; r++)
debug("sub%d = \"%.*s\"\n", r, ovector[r*2+1]-ovector[r*2], buffer(start+c+ovector[r*2]));
debug("rest = \"%s\"\n", buffer(start+c+ovector[1]));
debug("sub%d = \"%s\"\n", r, buffer.expand(start+c+ovector[r*2], ovector[r*2+1]-ovector[r*2])());
debug("after = \"%s\"\n", buffer.expand(start+c+ovector[1])());
s = subst;
debug("subs = \"%s\"\n", s.expand()());
debug("subs = \"%s\"\n", s.expand()());
for (r = 0; r < s.length(); r++)
{
debug("check \"%s\"\n", s.expand(r)());
if (s[r] == esc)
{
unsigned char ch = s[r+1];
if (ch < 9) // escaped 0 - 9 : replace with subexpr
if (c != 0 && ch < rc) // escaped 1 - 9 : replace with subexpr
{
ch *= 2;
rl = ovector[ch+1] - ovector[ch];
debug("replace \\%d: \"%.*s\"\n", ch/2, rl, buffer(start+c+ovector[ch]));
debug("replace \\%d: \"%s\"\n", ch/2, buffer.expand(start+c+ovector[ch], rl)());
s.replace(r, 2, buffer(start+c+ovector[ch]), rl);
r += rl - 1;
}
@ -214,16 +221,16 @@ static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
}
else if (s[r] == '&') // unescaped & : replace with match
{
debug("replace &: \"%.*s\"\n", l, buffer(start+c+ovector[0]));
debug("replace &: \"%s\"\n", buffer.expand(start+c+ovector[0], l)());
s.replace(r, 1, buffer(start+c+ovector[0]), l);
r += l - 1;
}
else continue;
debug("subs = \"%s\"\n", s());
debug("subs = \"%s\"\n", s.expand()());
}
buffer.replace(start+c+ovector[0], l, s);
length += s.length() - l;
c += s.length();
c += ovector[0] + s.length();
if (n == fmt.prec) // max match reached
return;
}

View File

@ -296,7 +296,7 @@ StreamBuffer StreamBuffer::expand(ssize_t start, ssize_t length) const
for (i = start; i < end; i++)
{
c = buffer[i];
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
if (c < 0x20 || c >= 0x7f)
{
result.print("<%02x>", c & 0xff);
}

View File

@ -460,7 +460,7 @@ finishProtocol(ProtocolResult status)
status = Fault;
}
//// flags &= ~(AcceptInput|AcceptEvent);
if (runningHandler)
if (runningHandler || flags & InitRun)
{
// get original error status
if (status == Success) status = runningHandler;

View File

@ -196,8 +196,12 @@ extern "C" long streamReload(char* recordname)
dbCommon* record;
long status;
int oldStreamError = streamError;
streamError = 1;
if(!pdbbase) {
error("No database has been loaded\n");
streamError = oldStreamError;
return ERROR;
}
debug("streamReload(%s)\n", recordname);
@ -234,6 +238,7 @@ extern "C" long streamReload(char* recordname)
}
dbFinishEntry(&dbentry);
StreamProtocolParser::free();
streamError = oldStreamError;
return OK;
}
@ -408,6 +413,7 @@ long streamInit(int after)
{
if (after)
{
streamError = 0; // Switch off errors after init in order not to spam messages when a device is down.
StreamProtocolParser::free();
}
return OK;

View File

@ -23,7 +23,7 @@
#include <stdio.h>
int streamDebug = 0;
int streamError = 0;
int streamError = 1;
extern "C" {
#ifdef _WIN32
__declspec(dllexport)

View File

@ -512,20 +512,31 @@ Each time newline is read, line is incremented.
buffer(token));
return false;
}
if (c == '$' && buffer[-1] == '\\')
{
// quoted variable reference
// terminate string here and do variable in next pass
buffer[-1] = quote;
ungetc(c, file);
break;
}
buffer.append(c);
if (c == quote && buffer[-2] != '\\')
if (c == quote)
{
quote = false;
break;
}
if (c == '\\')
{
c = getc(file);
if (c == '$')
{
// quoted variable reference
// terminate string here and do variable in next pass
buffer[-1] = quote;
ungetc(c, file);
break;
}
if (c == EOF || c == '\n')
{
error(line, filename(), "Backslash at end of line: %s\n",
buffer(token));
return false;
}
buffer.append(c);
}
c = getc(file);
}
buffer.append('\0').append(&l, sizeof(l)); // append line number

View File

@ -23,7 +23,7 @@
#define STREAM_MAJOR 2
#define STREAM_MINOR 7
#define STREAM_PATCHLEVEL 8
#define STREAM_PATCHLEVEL 13
#if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h>
@ -58,7 +58,6 @@ extern "C" {
#include <dbCommon.h>
#include <dbScan.h>
#include <devSup.h>
/* #include <dbFldTypes.h> */
#include <dbAccess.h>
#ifdef devStream_epicsExportSharedSymbols

View File

@ -55,11 +55,14 @@ static long readData (dbCommon *record, format_t *format)
ai->rval = rval;
if (ai->linr == menuConvertNO_CONVERSION)
{
/* allow more bits than 32 */
/* allow integers with more than 32 bits */
double val;
if (format->type == DBF_ULONG)
ai->val = (unsigned long)rval;
val = (unsigned long)rval;
else
ai->val = rval;
val = rval;
if (ai->aslo != 0.0 && ai->aslo != 1.0) val *= ai->aslo;
ai->val = val + ai->aoff;
return DO_NOT_CONVERT;
}
return OK;

View File

@ -46,11 +46,14 @@ static long readData (dbCommon *record, format_t *format)
ao->rval = rval;
if (ao->linr == menuConvertNO_CONVERSION)
{
/* allow more bits than 32 */
/* allow integers with more than 32 bits */
double val;
if (format->type == DBF_ULONG)
ao->val = (unsigned long)rval;
val = (unsigned long)rval;
else
ao->val = rval;
val = rval;
if (ao->aslo != 0.0 && ao->aslo != 1.0) val *= ao->aslo;
ao->val = val + ao->aoff;
return DO_NOT_CONVERT;
}
return OK;
@ -63,20 +66,21 @@ static long writeData (dbCommon *record, format_t *format)
{
aoRecord *ao = (aoRecord *) record;
double val = (INIT_RUN ? ao->val : ao->oval) - ao->aoff;
if (ao->aslo != 0.0 && ao->aslo != 1.0) val /= ao->aslo;
switch (format->type)
{
case DBF_DOUBLE:
{
double val = (INIT_RUN ? ao->val : ao->oval) - ao->aoff;
if (ao->aslo != 0.0 && ao->aslo != 1.0) val /= ao->aslo;
return streamPrintf (record, format, val);
}
case DBF_ULONG:
{
if (ao->linr == menuConvertNO_CONVERSION)
{
/* allow more bits than 32 */
return streamPrintf (record, format, (unsigned long)(INIT_RUN ? ao->val : ao->oval));
/* allow integers with more than 32 bits */
return streamPrintf (record, format, (unsigned long)val);
}
return streamPrintf (record, format, (unsigned long)ao->rval);
}
@ -84,8 +88,8 @@ static long writeData (dbCommon *record, format_t *format)
{
if (ao->linr == menuConvertNO_CONVERSION)
{
/* allow more bits than 32 */
return streamPrintf (record, format, (long)(INIT_RUN ? ao->val : ao->oval));
/* allow integers with more than 32 bits */
return streamPrintf (record, format, (long)val);
}
return streamPrintf (record, format, (long)ao->rval);
}