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;