diff --git a/doc/formats.html b/doc/formats.html
index 5cb5d04..4f9506e 100644
--- a/doc/formats.html
+++ b/doc/formats.html
@@ -352,56 +352,85 @@ In input, the next byte or bytes must match the checksum.
Implemented checksum functions
- %<SUM>
or %<SUM8>
+ %<sum>
or %<sum8>
- One byte. The sum of all characters modulo 28.
- %<SUM16>
+ %<sum16>
- Two bytes. The sum of all characters modulo 216.
- %<SUM32>
+ %<sum32>
- Four bytes. The sum of all characters modulo 232.
- %<NEGSUM>
or %<NSUM>
or %<-SUM>
+ %<negsum>
, %<nsum>
, %<-sum>
, %<negsum8>
, %<nsum8>
, or %<-sum8>
- One byte. The negative of the sum of all characters modulo 28.
- %<NOTSUM>
or %<~SUM>
+ %<negsum16>
, %<nsum16>
, or %<-sum16>
+ - Two bytes. The negative of the sum of all characters modulo 216.
+ %<negsum32>
, %<nsum32>
, or %<-sum32>
+ - Four bytes. The negative of the sum of all characters modulo 232.
+ %<notsum>
or %<~sum>
- One byte. The bitwise inverse of the sum of all characters modulo 28.
- %<XOR>
+ %<xor>
- One byte. All characters xor'ed.
- %<CRC8>
- - One byte. An often used 8 bit CRC checksum
+
%<xor7>
+ - One byte. All characters xor'ed & 0x7F.
+ %<crc8>
+ - One byte. An often used 8 bit crc checksum
(poly=0x07, init=0x00, xorout=0x00).
- %<CCITT8>
- - One byte. The CCITT standard 8 bit CRC checksum
+
%<ccitt8>
+ - One byte. The CCITT standard 8 bit crc checksum
(poly=0x31, init=0x00, xorout=0x00).
- %<CRC16>
- - Two bytes. An often used 16 bit CRC checksum
+
%<crc16>
+ - Two bytes. An often used 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).
- %<CRC16R>
- - Two bytes. An often used reflected 16 bit CRC checksum
+
%<crc16r>
+ - Two bytes. An often used reflected 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).
- %<CCITT16>
+ %<ccitt16>
- Two bytes. The usual (but wrong?)
- implementation of the CCITT standard 16 bit CRC checksum
+ implementation of the CCITT standard 16 bit crc checksum
(poly=0x1021, init=0xFFFF, xorout=0x0000).
- %<CCITT16A>
+ %<ccitt16a>
- Two bytes. The unusual (but correct?)
- implementation of the CCITT standard 16 bit CRC checksum with augment.
+ implementation of the CCITT standard 16 bit crc checksum with augment.
(poly=0x1021, init=0x1D0F, xorout=0x0000).
- %<CRC32>
- - Four bytes. The standard 32 bit CRC checksum.
+
%<crc32>
+ - Four bytes. The standard 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).
- %<CRC32R>
- - Four bytes. The standard reflected 32 bit CRC checksum.
+
%<crc32r>
+ - Four bytes. The standard reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).
- %<JAMCRC>
- - Four bytes. Another reflected 32 bit CRC checksum.
+
%<jamcrc>
+ - Four bytes. Another reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).
- %<ADLER32>
+ %<adler32>
- Four bytes. The Adler32 checksum according to RFC 1950.
- %<HEXSUM8>
+ %<hexsum8>
- One byte. The sum of all hex digits. (Other characters are ignored.)
+
+12. Regular Expresion STRING Converter (%/regex/
)
+
+This input-only format matches Perl compatible regular expressions (PCRE).
+It is only available if a PCRE library is installed and the RELEASE
+file contains the variable PCRE
which should point to the
+install location.
+If the regular expression is not anchored, i.e. does not start with
+^
, leading non-matching input is skipped.
+A maximum of width bytes is matched, if specified.
+If prec is given, it specifies the sub-expression 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 /
is must be escaped.
+
+
+Example: %.1/<title>(.*)<\/title>/
returns
+the title of an HTML page, skipps anything before the
+<title>
tag and leaves anything after the
+</title>
tag in the input buffer.
+
Next: Record Processing
Dirk Zimoch, 2007
diff --git a/doc/nav.html b/doc/nav.html
index cc2ed0b..d756766 100644
--- a/doc/nav.html
+++ b/doc/nav.html
@@ -120,6 +120,7 @@ h1 {font-size:120%;
%r
%D
%<checksum>
+ %/regex/
diff --git a/src/AsynDriverInterface.cc b/src/AsynDriverInterface.cc
index 11b4e28..33d63b4 100644
--- a/src/AsynDriverInterface.cc
+++ b/src/AsynDriverInterface.cc
@@ -934,6 +934,7 @@ void intrCallbackOctet(void* /*pvt*/, asynUser *pasynUser,
// 3. eomReason=ASYN_EOM_CNT when message was too long for
// internal buffer of asynDriver.
+ if (!interruptAccept) return; // too early to process records
if (interface->ioAction == AsyncRead ||
interface->ioAction == AsyncReadMore)
{
@@ -1181,6 +1182,9 @@ connectRequest(unsigned long connecttimeout_ms)
double queueTimeout = connecttimeout_ms*0.001;
asynStatus status;
ioAction = Connect;
+
+ debug("AsynDriverInterface::connectRequest %s\n",
+ clientName());
status = pasynManager->queueRequest(pasynUser,
asynQueuePriorityConnect, queueTimeout);
if (status != asynSuccess)
@@ -1198,14 +1202,22 @@ connectRequest(unsigned long connecttimeout_ms)
void AsynDriverInterface::
connectHandler()
{
+ int connected;
asynStatus status;
- status = pasynCommon->connect(pvtCommon, pasynUser);
- if (status != asynSuccess)
+
+ pasynManager->isConnected(pasynUser, &connected);
+ debug("AsynDriverInterface::connectHandler %s is %s connected\n",
+ clientName(), connected ? "already" : "not yet");
+ if (!connected)
{
- error("%s connectRequest: pasynCommon->connect() failed: %s\n",
- clientName(), pasynUser->errorMessage);
- connectCallback(StreamIoFault);
- return;
+ status = pasynCommon->connect(pvtCommon, pasynUser);
+ if (status != asynSuccess)
+ {
+ error("%s connectRequest: pasynCommon->connect() failed: %s\n",
+ clientName(), pasynUser->errorMessage);
+ connectCallback(StreamIoFault);
+ return;
+ }
}
connectCallback(StreamIoSuccess);
}
@@ -1215,6 +1227,9 @@ disconnect()
{
asynStatus status;
ioAction = Disconnect;
+
+ debug("AsynDriverInterface::disconnect %s\n",
+ clientName());
status = pasynManager->queueRequest(pasynUser,
asynQueuePriorityConnect, 0.0);
if (status != asynSuccess)
@@ -1233,13 +1248,21 @@ disconnect()
void AsynDriverInterface::
disconnectHandler()
{
+ int connected;
asynStatus status;
- status = pasynCommon->disconnect(pvtCommon, pasynUser);
- if (status != asynSuccess)
+
+ pasynManager->isConnected(pasynUser, &connected);
+ debug("AsynDriverInterface::disconnectHandler %s is %s disconnected\n",
+ clientName(), !connected ? "already" : "not yet");
+ if (connected)
{
- error("%s connectRequest: pasynCommon->disconnect() failed: %s\n",
- clientName(), pasynUser->errorMessage);
- return;
+ status = pasynCommon->disconnect(pvtCommon, pasynUser);
+ if (status != asynSuccess)
+ {
+ error("%s connectRequest: pasynCommon->disconnect() failed: %s\n",
+ clientName(), pasynUser->errorMessage);
+ return;
+ }
}
}
@@ -1312,6 +1335,8 @@ void handleTimeout(asynUser* pasynUser)
interface->connectCallback(StreamIoTimeout);
break;
case Disconnect:
+ debug ("AsynDriverInterface %s: disconnect timeout\n",
+ interface->clientName());
// not interested in callback
break;
// No AsyncRead here because we don't use timeout when polling
diff --git a/src/CONFIG_STREAM b/src/CONFIG_STREAM
index d487bd9..13b8ecf 100644
--- a/src/CONFIG_STREAM
+++ b/src/CONFIG_STREAM
@@ -41,6 +41,10 @@ FORMATS += Raw
FORMATS += Binary
FORMATS += Checksum
+ifdef PCRE
+FORMATS += Regexp
+endif
+
# Want a loadable module?
# For Tornado 2.0.2, a fix is needed in the
# registerRecordDeviceDriver.pl script in base:
diff --git a/src/ChecksumConverter.cc b/src/ChecksumConverter.cc
index 2a5ea72..dd531d1 100644
--- a/src/ChecksumConverter.cc
+++ b/src/ChecksumConverter.cc
@@ -50,6 +50,11 @@ static ulong xor8(const uchar* data, ulong len, ulong sum)
return sum;
}
+static ulong xor7(const uchar* data, ulong len, ulong sum)
+{
+ return xor8(data, len, sum) & 0x7F;
+}
+
static ulong crc_0x07(const uchar* data, ulong len, ulong crc)
{
// x^8 + x^2 + x^1 + x^0 (0x07)
@@ -460,12 +465,23 @@ static checksum checksumMap[] =
{"sum8", sum, 0x00, 0x00, 1}, // 0xDD
{"sum16", sum, 0x0000, 0x0000, 2}, // 0x01DD
{"sum32", sum, 0x00000000, 0x00000000, 4}, // 0x000001DD
- {"nsum", sum, 0xff, 0xff, 1}, // 0x23
- {"negsum", sum, 0xff, 0xff, 1}, // 0x23
- {"-sum", sum, 0xff, 0xff, 1}, // 0x23
- {"notsum", sum, 0x00, 0xff, 1}, // 0x22
- {"~sum", sum, 0x00, 0xff, 1}, // 0x22
+ {"nsum", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"negsum", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"-sum", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"nsum8", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"negsum8", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"-sum8", sum, 0xFF, 0xFF, 1}, // 0x23
+ {"nsum16", sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
+ {"negsum16",sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
+ {"-sum16", sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
+ {"nsum32", sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
+ {"negsum32",sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
+ {"-sum32", sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
+ {"notsum", sum, 0x00, 0xFF, 1}, // 0x22
+ {"~sum", sum, 0x00, 0xFF, 1}, // 0x22
{"xor", xor8, 0x00, 0x00, 1}, // 0x31
+ {"xor8", xor8, 0x00, 0x00, 1}, // 0x31
+ {"xor7", xor7, 0x00, 0x00, 1}, // 0x31
{"crc8", crc_0x07, 0x00, 0x00, 1}, // 0xF4
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
@@ -494,7 +510,7 @@ parse(const StreamFormat&, StreamBuffer& info, const char*& source, bool)
const char* p = strchr(source, '>');
if (!p)
{
- error ("Missing terminating '>' in checksum format.\n");
+ error ("Missing closing '>' in checksum format.\n");
return false;
}
diff --git a/src/Makefile b/src/Makefile
index 7940e8c..bd6ef1f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -57,6 +57,10 @@ SRCS += $(FORMATS:%=%Converter.cc)
SRCS += $(RECORDS:%=dev%Stream.c)
SRCS += $(STREAM_SRCS)
+ifneq ($(filter Regexp,$(FORMATS)),)
+LIB_LIBS += pcre
+endif
+
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
ifeq ($(USE_MEMGUARD),YES)
diff --git a/src/RegexpConverter.cc b/src/RegexpConverter.cc
new file mode 100644
index 0000000..f9cfada
--- /dev/null
+++ b/src/RegexpConverter.cc
@@ -0,0 +1,125 @@
+/***************************************************************
+* StreamDevice Support *
+* *
+* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
+* (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) *
+* *
+* This is the regexp format converter of StreamDevice. *
+* Please refer to the HTML files in ../doc/ for a detailed *
+* documentation. *
+* *
+* If you do any changes in this file, you are not allowed to *
+* redistribute it any more. If there is a bug or a missing *
+* feature, send me an email and/or your patch. If I accept *
+* your changes, they will go to the next release. *
+* *
+* DISCLAIMER: If this software breaks something or harms *
+* someone, it's your problem. *
+* *
+***************************************************************/
+
+#include "StreamFormatConverter.h"
+#include "StreamError.h"
+#include "string.h"
+#include "pcre.h"
+
+// Perl regular expressions (PCRE) %/regexp/
+
+/* Notes:
+ - Memory for compiled regexp is allocated in parse but never freed.
+ This should not be too much of a problem unless streamReload is
+ called really often before the IOC is restarted. It is not a
+ 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
+{
+ int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
+ int scanString(const StreamFormat&, const char*, char*, size_t);
+};
+
+int RegexpConverter::
+parse(const StreamFormat& fmt, StreamBuffer& info,
+ const char*& source, bool scanFormat)
+{
+ if (!scanFormat)
+ {
+ error("Format conversion %%/regexp/ is only allowed in input formats\n");
+ return false;
+ }
+ if (fmt.prec > 9)
+ {
+ error("Subexpression index %d too big (>9)\n", fmt.prec);
+ return false;
+ }
+ if (fmt.flags & (left_flag|space_flag|zero_flag|alt_flag))
+ {
+ error("Use of modifiers '-', ' ', '0', '#'"
+ "not allowed with %%/regexp/ conversion\n");
+ return false;
+ }
+ StreamBuffer pattern;
+ while (*source != '/')
+ {
+ if (!*source) {
+ error("Missing closing '/' after %%/ format conversion\n");
+ return false;
+ }
+ if (*source == esc) {
+ source++;
+ pattern.printf("\\x%02x", *source++ & 0xFF);
+ continue;
+ }
+ pattern.append(*source++);
+ }
+ source++;
+ debug("regexp = \"%s\"\n", pattern());
+ const char* errormsg;
+ int eoffset;
+ pcre* code = pcre_compile(pattern(), 0,
+ &errormsg, &eoffset, NULL);
+ if (!code)
+ {
+ error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
+ return false;
+ }
+ info.append(&code, sizeof(code));
+ return string_format;
+}
+
+int RegexpConverter::
+scanString(const StreamFormat& fmt, const char* input,
+ char* value, size_t maxlen)
+{
+ pcre* code;
+ size_t len;
+ int ovector[30];
+ int rc;
+ int subexpr = 0;
+
+ memcpy (&code, fmt.info, sizeof(code));
+
+ len = fmt.width > 0 ? fmt.width : strlen(input);
+ subexpr = fmt.prec > 0 ? fmt.prec : 0;
+ rc = pcre_exec(code, NULL, input, len, 0, 0, ovector, 30);
+ if (rc < 1) return -1;
+ if (fmt.flags & skip_flag) return ovector[1];
+ len = ovector[subexpr*2+1] - ovector[subexpr*2];
+ if (len >= maxlen) {
+ if (!(fmt.flags & sign_flag)) {
+ debug("Matching string \"%s\" too long (%d>%d bytes)\n",
+ StreamBuffer(input+ovector[subexpr*2], len).expand()(),
+ len, maxlen-1);
+ return -1;
+ }
+ len = maxlen-1;
+ }
+ memcpy(value, input+ovector[subexpr*2], len);
+ value[len]=0;
+ return ovector[1];
+}
+
+RegisterConverter (RegexpConverter, "/");
diff --git a/src/StreamCore.cc b/src/StreamCore.cc
index 47ee904..2c85381 100644
--- a/src/StreamCore.cc
+++ b/src/StreamCore.cc
@@ -30,7 +30,7 @@ const char* commandStr[] = { "end", "in", "out", "wait", "event", "exec",
inline const char* commandName(unsigned char i)
{
- return i > exec_cmd ? "invalid" : commandStr[i];
+ return i >= sizeof(commandStr)/sizeof(char*) ? "invalid" : commandStr[i];
}
/// debug functions /////////////////////////////////////////////
@@ -1323,7 +1323,7 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
consumed > inputLine.length()-consumedInput) return -1;
#ifndef NO_TEMPORARY
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
- name(), StreamBuffer(value, consumed).expand()());
+ name(), StreamBuffer(value, maxlen).expand()());
#endif
flags |= GotValue;
return consumed;
diff --git a/src/StreamFormatConverter.cc b/src/StreamFormatConverter.cc
index e4f9d80..ea77ab5 100644
--- a/src/StreamFormatConverter.cc
+++ b/src/StreamFormatConverter.cc
@@ -384,7 +384,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
fmt.prec, fmt.conv);
return false;
}
- info.printf("%%%d[", fmt.width);
+ info.printf("%%%s%d[", fmt.flags & skip_flag ? "*" : "", fmt.width);
while (*source && *source != ']')
{
if (*source == esc) source++;
diff --git a/src/StreamProtocol.cc b/src/StreamProtocol.cc
index 3781b6e..d3a5836 100644
--- a/src/StreamProtocol.cc
+++ b/src/StreamProtocol.cc
@@ -1332,18 +1332,42 @@ compileString(StreamBuffer& buffer, const char*& source,
// try constant token
struct {const char* name; char code;} codes [] =
{
- {"skip", skip},
- {"?", skip},
- {"eot", 4 },
- {"ack", 6 },
- {"bel", 7 },
- {"bs", 8 },
- {"ht", 9 },
- {"tab", 9 },
- {"lf", '\n' },
- {"nl", '\n' },
- {"cr", '\r' },
- {"esc", esc },
+ {"skip", skip},
+ {"?", skip},
+ {"nul", 0x00},
+ {"soh", 0x01},
+ {"stx", 0x02},
+ {"etx", 0x03},
+ {"eot", 0x04},
+ {"enq", 0x05},
+ {"ack", 0x06},
+ {"bel", 0x07},
+ {"bs", 0x08},
+ {"ht", 0x09},
+ {"tab", 0x09},
+ {"lf", 0x0A},
+ {"nl", 0x0A},
+ {"vt", 0x0B},
+ {"ff", 0x0C},
+ {"cr", 0x0D},
+ {"so", 0x0E},
+ {"si", 0x0F},
+ {"dle", 0x10},
+ {"dc1", 0x11},
+ {"dc2", 0x12},
+ {"dc3", 0x13},
+ {"dc4", 0x14},
+ {"nak", 0x15},
+ {"syn", 0x16},
+ {"etb", 0x17},
+ {"can", 0x18},
+ {"em", 0x19},
+ {"sub", 0x1A},
+ {"esc", 0x1B},
+ {"fs", 0x1C},
+ {"gs", 0x1D},
+ {"rs", 0x1E},
+ {"us", 0x1F},
{"del", 0x7f}
};
size_t i;