Compare commits

...

14 Commits

Author SHA1 Message Date
d40c9610c9 %B fixed 2007-11-30 16:48:20 +00:00
b97948a704 %B fixed 2007-11-30 16:45:32 +00:00
7ac080b8d7 version update 2007-11-30 15:36:41 +00:00
7a83d91ec9 better PCRE support 2007-11-30 15:33:40 +00:00
ad4b84fd60 disconnect callback 2007-11-30 15:33:22 +00:00
1f82b0224a *** empty log message *** 2007-11-30 15:32:10 +00:00
627035a2ea *** empty log message *** 2007-11-30 15:26:52 +00:00
6bb963e852 better PCRE support 2007-11-30 15:26:26 +00:00
e6c0b6af0e support signed 2007-11-30 15:25:00 +00:00
55c8977e7b Don't abort on big input 2007-11-30 15:24:38 +00:00
ed8b3aded1 disconnect callback 2007-11-30 15:23:13 +00:00
161f3c7d3c copy from main project 2007-11-30 15:18:16 +00:00
b1395f43f2 *** empty log message *** 2007-11-30 15:17:56 +00:00
c3979f766f update to current snapshot version 2007-08-27 12:49:48 +00:00
40 changed files with 575 additions and 359 deletions

View File

@ -1,11 +1,9 @@
include /ioc/tools/driver.makefile
#EXCLUDE_VERSIONS = 3.13.2
EXCLUDE_VERSIONS = 3.13
PROJECT=stream
EXCLUDE_VERSIONS = 3.13.2
PROJECT=stream2
BUILDCLASSES += Linux
DOCUDIR = doc
SOURCES += $(wildcard src/*.c)
SOURCES += $(wildcard src/*.cc)
DBDS = stream.dbd
@ -15,6 +13,8 @@ FORMATS += BCD
FORMATS += Raw
FORMATS += Binary
FORMATS += Checksum
FORMATS += Regexp
FORMATS += Exponential
RECORDTYPES += aai aao
RECORDTYPES += ao ai
RECORDTYPES += bo bi
@ -25,6 +25,16 @@ RECORDTYPES += stringout stringin
RECORDTYPES += waveform
RECORDTYPES += calcout
SOURCES += $(RECORDTYPES:%=src/dev%Stream.c)
SOURCES += $(FORMATS:%=src/%Converter.cc)
SOURCES += $(BUSSES:%=src/%Interface.cc)
SOURCES += $(wildcard src/Stream*.cc)
SOURCES += src/StreamVersion.c
ifeq (${EPICS_BASETYPE},3.13)
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
endif
StreamCore.o: streamReferences
streamReferences:

View File

@ -172,6 +172,9 @@ void <a href="#event">eventCallback</a>(StreamIoStatus&nbsp;status);
<div class="indent"><code>
void <a href="#connect">connectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#connect">disconnectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<h3>Other provided methods, attibutes, and types</h3>
@ -280,6 +283,9 @@ bool disconnect();
<div class="indent"><code>
void connectCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void disconnectCallback(IoStatus&nbsp;status);
</code></div>
<p>
Connection should be handled automatically.
If the device is disconnected, each attempt to access the
@ -292,7 +298,8 @@ However, sometimes the client wants to connect or
disconnect explicitely.
To connect, the client calls <code>connectRequest()</code>.
This function should return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
or <code>false</code> if the request cannot be accepted or connection
handling is not supported.
The interface should call <code>connectCallback(StreamIoSuccess)</code>
once the bus could be connected.
If the bus cannot be connected within <code>connecttimeout_ms</code>
@ -305,10 +312,14 @@ something wrong with the I/O hardware,
<code>connectCallback(StreamIoFault)</code> may be called.
</p>
<p>
To disconnect, the client calls <code>disconnect()</code>;
To disconnect, the client calls <code>disconnectRequest()</code>;
This function should return <code>true</code> immediately or
<code>false</code> if disconnecting is impossible.
There is no callback for <code>disconnect()</code>.
<code>false</code> if the request cannot be accepted or connection
handling is not supported.
The interface should call <code>connectCallback(StreamIoSuccess)</code>
once the bus is disconnected. There is no timeout for this operation.
If disconnecting is impossible, the interface should call
<code>connectCallback(StreamIoFault)</code>.
</p>
<a name="lock"></a>

View File

@ -352,56 +352,105 @@ In input, the next byte or bytes must match the checksum.
<h3>Implemented checksum functions</h3>
<dl>
<dt><code>%&lt;SUM&gt;</code> or <code>%&lt;SUM8&gt;</code></dt>
<dt><code>%&lt;sum&gt;</code> or <code>%&lt;sum8&gt;</code></dt>
<dd>One byte. The sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;SUM16&gt;</code></dt>
<dt><code>%&lt;sum16&gt;</code></dt>
<dd>Two bytes. The sum of all characters modulo 2<sup>16</sup>.</dd>
<dt><code>%&lt;SUM32&gt;</code></dt>
<dt><code>%&lt;sum32&gt;</code></dt>
<dd>Four bytes. The sum of all characters modulo 2<sup>32</sup>.</dd>
<dt><code>%&lt;NEGSUM&gt;</code> or <code>%&lt;NSUM&gt;</code> or <code>%&lt;-SUM&gt;</code></dt>
<dt><code>%&lt;negsum&gt;</code>, <code>%&lt;nsum&gt;</code>, <code>%&lt;-sum&gt;</code>, <code>%&lt;negsum8&gt;</code>, <code>%&lt;nsum8&gt;</code>, or <code>%&lt;-sum8&gt;</code></dt>
<dd>One byte. The negative of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;NOTSUM&gt;</code> or <code>%&lt;~SUM&gt;</code></dt>
<dt><code>%&lt;negsum16&gt;</code>, <code>%&lt;nsum16&gt;</code>, or <code>%&lt;-sum16&gt;</code></dt>
<dd>Two bytes. The negative of the sum of all characters modulo 2<sup>16</sup>.</dd>
<dt><code>%&lt;negsum32&gt;</code>, <code>%&lt;nsum32&gt;</code>, or <code>%&lt;-sum32&gt;</code></dt>
<dd>Four bytes. The negative of the sum of all characters modulo 2<sup>32</sup>.</dd>
<dt><code>%&lt;notsum&gt;</code> or <code>%&lt;~sum&gt;</code></dt>
<dd>One byte. The bitwise inverse of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;XOR&gt;</code></dt>
<dt><code>%&lt;xor&gt;</code></dt>
<dd>One byte. All characters xor'ed.</dd>
<dt><code>%&lt;CRC8&gt;</code></dt>
<dd>One byte. An often used 8 bit CRC checksum
<dt><code>%&lt;xor7&gt;</code></dt>
<dd>One byte. All characters xor'ed &amp; 0x7F.</dd>
<dt><code>%&lt;crc8&gt;</code></dt>
<dd>One byte. An often used 8 bit crc checksum
(poly=0x07, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CCITT8&gt;</code></dt>
<dd>One byte. The CCITT standard 8 bit CRC checksum
<dt><code>%&lt;ccitt8&gt;</code></dt>
<dd>One byte. The CCITT standard 8 bit crc checksum
(poly=0x31, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CRC16&gt;</code></dt>
<dd>Two bytes. An often used 16 bit CRC checksum
<dt><code>%&lt;crc16&gt;</code></dt>
<dd>Two bytes. An often used 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CRC16R&gt;</code></dt>
<dd>Two bytes. An often used reflected 16 bit CRC checksum
<dt><code>%&lt;crc16r&gt;</code></dt>
<dd>Two bytes. An often used reflected 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CCITT16&gt;</code></dt>
<dt><code>%&lt;ccitt16&gt;</code></dt>
<dd>Two bytes. The usual (but <a target="ex"
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
implementation of the CCITT standard 16 bit CRC checksum
implementation of the CCITT standard 16 bit crc checksum
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
<dt><code>%&lt;CCITT16A&gt;</code></dt>
<dt><code>%&lt;ccitt16a&gt;</code></dt>
<dd>Two bytes. The unusual (but <a target="ex"
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
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).</dd>
<dt><code>%&lt;CRC32&gt;</code></dt>
<dd>Four bytes. The standard 32 bit CRC checksum.
<dt><code>%&lt;crc32&gt;</code></dt>
<dd>Four bytes. The standard 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;CRC32R&gt;</code></dt>
<dd>Four bytes. The standard reflected 32 bit CRC checksum.
<dt><code>%&lt;crc32r&gt;</code></dt>
<dd>Four bytes. The standard reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;JAMCRC&gt;</code></dt>
<dd>Four bytes. Another reflected 32 bit CRC checksum.
<dt><code>%&lt;jamcrc&gt;</code></dt>
<dd>Four bytes. Another reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
<dt><code>%&lt;ADLER32&gt;</code></dt>
<dt><code>%&lt;adler32&gt;</code></dt>
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
<dt><code>%&lt;HEXSUM8&gt;</code></dt>
<dt><code>%&lt;hexsum8&gt;</code></dt>
<dd>One byte. The sum of all hex digits. (Other characters are ignored.)</dd>
</dl>
<a name="regex"></a>
<h2>12. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
<p>
This input-only format matches <a target="ex"
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
It is only available if a PCRE library is installed.
</p>
<p>
If PCRE is not available for your host or cross architecture, download
the sourcecode from <a target="ex" href="http://www.pcre.org/">www.pcre.org</a>
and try my EPICS compatible <a target="ex"
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
to compile it like a normal EPICS application.
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
In your RELEASE file define the variable <code>PCRE</code> so that
it points to the install location of PCRE.
</p>
<p>
If PCRE is already installed on your system, use the variables
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
the install directories of <code>pcre.h</code> and the library.
</p>
<p>
If you have PCRE installed in different locations for different (cross)
architectures, define the variables in RELEASE.Common.&lt;architecture&gt;
instead of the global RELEASE file.
</p>
<p>
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>prec</em> 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 <code>/</code> is must be escaped.
</p>
<p>
Example: <code>%.1/&lt;title&gt;(.*)&lt;\/title&gt;/</code> returns
the title of an HTML page, skipps anything before the
<code>&lt;title&gt</code> tag and leaves anything after the
<code>&lt;/title&gt;</code> tag in the input buffer.
</p>
<hr>
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
<p><small>Dirk Zimoch, 2007</small></p>

View File

@ -120,6 +120,7 @@ h1 {font-size:120%;
<li><a target="text" href="formats.html#raw">%r</a></li>
<li><a target="text" href="formats.html#bcd">%D</a></li>
<li><a target="text" href="formats.html#chksum">%&lt;<em>checksum</em>&gt;</a></li>
<li><a target="text" href="formats.html#regex">%/<em>regex</em>/</a></li>
</ul>
</div>
<div class="top">

View File

@ -36,6 +36,8 @@ href="http://www.aps.anl.gov/aod/bcda/synApps/index.php"
Up to <em>calc</em> release R2-6 (<em>synApps</em> release R5_1),
the <em>scalcout</em> record needs a fix.
(See separate <a href="scalcout.html"><em>scalcout</em> page</a>.)
Support for the scalcout is optional. <em>StreamDevice</em> works
as well without scalcout or SynApps.
</p>
<p>
Up to release R3.14.8.2, a fix in EPICS base is required to build
@ -48,15 +50,40 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
</pre>
<p>
Make sure that the <em>asyn</em> library and the <em>calc</em> module of
<em>synApps</em> can be found, e.g. by
Make sure that the <em>asyn</em> library (and the <em>calc</em> module of
<em>synApps</em>, if desired) can be found, e.g. by
adding <code>ASYN</code>
and (if installed) <code>CALC</code> or <code>SYNAPPS</code>
to your <kbd>&lt;top&gt;/configure/RELEASE</kbd> file:
</p>
<pre>
ASYN=/home/epics/asyn/4-5
CALC=/home/epics/synApps/calc/2-7
</pre>
<p>
If you want to enable regular expression matching, you need the <em>PCRE</em> package.
For most Linux systems, it is already installed. In that case add the locations
of the <em>PCRE</em> header and library to your <kbd>RELEASE</kbd> file:
</p>
<pre>
PCRE_INCLUDE=/usr/include/pcre
PCRE_LIB=/usr/lib
</pre>
<p>
If you want to build <em>StreamDevice</em> for platforms without <em>PCRE</em> support,
it is the easiest to build <em>PCRE</em> as an EPICS application.
Download the <em>PCRE</em> package from <a target=ex href="http://www.pcre.org">www.pcre.org</a>
and compile it with my EPICS compatible
<a target=ex href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>.
Then define the location of the application in your RELEASE file.
</p>
<pre>
PCRE=/home/epics/pcre
</pre>
<p>
Regular expressions are optional. If you don't want them, you don't need this.
</p>
<p>
For details on <kbd>&lt;top&gt;</kbd> directories and RELEASE files,
please refer to the

View File

@ -186,7 +186,7 @@ class AsynDriverInterface : StreamBusInterface
bool supportsEvent();
bool supportsAsyncRead();
bool connectRequest(unsigned long connecttimeout_ms);
bool disconnect();
bool disconnectRequest();
void finish();
#ifdef EPICS_3_14
@ -462,11 +462,6 @@ lockRequest(unsigned long lockTimeout_ms)
clientName(), lockTimeout_ms);
asynStatus status;
lockTimeout = lockTimeout_ms ? lockTimeout_ms*0.001 : -1.0;
if (!lockTimeout_ms)
{
if (!connectToAsynPort()) return false;
}
ioAction = Lock;
status = pasynManager->queueRequest(pasynUser, priority(),
lockTimeout);
@ -497,6 +492,8 @@ connectToAsynPort()
clientName(), pasynUser->errorMessage);
return false;
}
debug("AsynDriverInterface::connectToAsynPort(%s) is %s connected\n",
clientName(), connected ? "already" : "not yet");
if (!connected)
{
status = pasynCommon->connect(pvtCommon, pasynUser);
@ -510,6 +507,12 @@ connectToAsynPort()
return false;
}
}
// We probably should set REN=1 prior to sending but this
// seems to hang up the device every other time.
// if (pasynGpib)
// {
// pasynGpib->ren(pvtGpib, pasynUser, 1);
// }
return true;
}
@ -517,10 +520,12 @@ connectToAsynPort()
void AsynDriverInterface::
lockHandler()
{
int connected;
debug("AsynDriverInterface::lockHandler(%s)\n",
clientName());
pasynManager->blockProcessCallback(pasynUser, false);
lockCallback(StreamIoSuccess);
connected = connectToAsynPort();
lockCallback(connected ? StreamIoSuccess : StreamIoFault);
}
// interface function: we don't need exclusive access any more
@ -934,6 +939,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 +1187,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,49 +1207,51 @@ connectRequest(unsigned long connecttimeout_ms)
void AsynDriverInterface::
connectHandler()
{
asynStatus status;
status = pasynCommon->connect(pvtCommon, pasynUser);
if (status != asynSuccess)
{
error("%s connectRequest: pasynCommon->connect() failed: %s\n",
clientName(), pasynUser->errorMessage);
connectCallback(StreamIoFault);
return;
}
connectCallback(StreamIoSuccess);
connectCallback(connectToAsynPort() ? StreamIoSuccess : StreamIoFault);
}
bool AsynDriverInterface::
disconnect()
disconnectRequest()
{
asynStatus status;
ioAction = Disconnect;
debug("AsynDriverInterface::disconnectRequest %s\n",
clientName());
status = pasynManager->queueRequest(pasynUser,
asynQueuePriorityConnect, 0.0);
if (status != asynSuccess)
{
error("%s disconnect: pasynManager->queueRequest() failed: %s\n",
error("%s disconnectRequest: pasynManager->queueRequest() failed: %s\n",
clientName(), pasynUser->errorMessage);
return false;
}
// continues with:
// handleRequest() -> disconnectHandler()
// or handleTimeout()
// (does not expect callback)
return true;
}
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);
disconnectCallback(StreamIoFault);
return;
}
}
disconnectCallback(StreamIoSuccess);
}
void AsynDriverInterface::
@ -1250,6 +1261,12 @@ finish()
clientName());
cancelTimer();
ioAction = None;
// if (pasynGpib)
// {
// // Release GPIB device the the end of the protocol
// // to re-enable local buttons.
// pasynGpib->ren(pvtGpib, pasynUser, 0);
// }
debug("AsynDriverInterface::finish(%s) done\n",
clientName());
}
@ -1312,7 +1329,9 @@ void handleTimeout(asynUser* pasynUser)
interface->connectCallback(StreamIoTimeout);
break;
case Disconnect:
// not interested in callback
error("AsynDriverInterface %s: disconnect timeout\n",
interface->clientName());
// should not happen because of infinite timeout
break;
// No AsyncRead here because we don't use timeout when polling
default:

View File

@ -38,11 +38,11 @@ parse(const StreamFormat& format, StreamBuffer& info,
if (format.conv == 'B')
{
// user defined characters for %B (next 2 in source)
if (!*source )
if (*source)
{
if (*source == esc) source++;
info.append(*source++);
if (!*source)
if (*source)
{
if (*source == esc) source++;
info.append(*source++);

View File

@ -40,6 +40,23 @@ FORMATS += BCD
FORMATS += Raw
FORMATS += Binary
FORMATS += Checksum
FORMATS += Exponential
# Want Perl regular expression matching?
# If PCRE is installed at the same location for all
# architectures, define the following variable in your
# RELEASE file. If the PCRE installation is different
# (or even missing) for some architectures, use
# separate RELEASE.Common.<arch> files.
# If PCRE is an EPICS-like project define
# PCRE=<location of the PCRE project>
# Else define
# PCRE_INCLUDE=<location of the pcre.h file>
# PCRE_LIB=<location of the PCRE library>
ifneq ($(words $(PCRE) $(PCRE_LIB) $(PCRE_INCLUDE)),0)
FORMATS += Regexp
endif
# Want a loadable module?
# For Tornado 2.0.2, a fix is needed in the

View File

@ -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;
}

View File

@ -0,0 +1,63 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is a custom exponential format converter for *
* StreamDevice. *
* The number is represented as two signed integers, mantissa *
* and exponent, like in +00011-01 *
* 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 <math.h>
// Exponential Converter %m: format +00351-02 means +351e-2
class ExponentialConverter : public StreamFormatConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual int scanDouble(const StreamFormat&, const char*, double&);
};
int ExponentialConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (!scanFormat)
{
error("At the moment for %%m format only input is implemented\n");
return false;
}
return double_format;
}
int ExponentialConverter::
scanDouble(const StreamFormat& fmt, const char* input, double& value)
{
int mantissa;
int exponent;
int length = -1;
sscanf(input, "%d%d%n", &mantissa, &exponent, &length);
if (fmt.flags & skip_flag) return length;
if (length == -1) return -1;
value = (double)(mantissa) * pow(10, exponent);
return length;
}
RegisterConverter (ExponentialConverter, "m");

View File

@ -2,7 +2,7 @@
# StreamDevice Support #
# #
# (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) #
# (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) #
# (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) #
# #
# This is the EPICS 3.14 Makefile of StreamDevice. #
# Normally it should not be necessary to modify this file. #
@ -57,6 +57,19 @@ SRCS += $(FORMATS:%=%Converter.cc)
SRCS += $(RECORDS:%=dev%Stream.c)
SRCS += $(STREAM_SRCS)
# find system wide or local PCRE header and library
ifdef PCRE_INCLUDE
RegexpConverter_INCLUDES += -I$(PCRE_INCLUDE)
endif
ifdef PCRE
LIB_LIBS += pcre
else
ifneq ($(words $(PCRE_LIB) $(PCRE_INCLUDE)),0)
LIB_SYS_LIBS += pcre
SHRLIB_DEPLIB_DIRS += $(PCRE_LIB)
endif
endif
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
ifeq ($(USE_MEMGUARD),YES)
@ -95,6 +108,7 @@ $(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
done > $@
@echo "driver(stream)" >> $@
@echo "variable(streamDebug, int)" >> $@
@echo "variable(showAsyncErrors, int)" >> $@
@echo "registrar(streamRegistrar)" >> $@
endif

View File

@ -54,7 +54,16 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
value >>= 8;
width--;
}
byte = (byte & 0x80) ? 0xFF : 0x00; // fill with sign
if (format.flags & zero_flag)
{
// fill with zero
byte = 0;
}
else
{
// fill with sign
byte = (byte & 0x80) ? 0xFF : 0x00;
}
while (width--)
{
output.append(byte);
@ -62,8 +71,17 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
}
else // msb first (big endian)
{
byte = ((value >> (8 * (prec-1))) & 0x80) ? 0xFF : 0x00;
while (width > prec) // fill with sign
if (format.flags & zero_flag)
{
// fill with zero
byte = 0;
}
else
{
// fill with sign
byte = ((value >> (8 * (prec-1))) & 0x80) ? 0xFF : 0x00;
}
while (width > prec)
{
output.append(byte);
width--;
@ -98,14 +116,32 @@ scanLong(const StreamFormat& format, const char* input, long& value)
}
if (width == 0)
{
val |= ((signed char) input[length++]) << shift;
if (format.flags & zero_flag)
{
// fill with zero
val |= ((unsigned char) input[length++]) << shift;
}
else
{
// fill with sign
val |= ((signed char) input[length++]) << shift;
}
}
length += width; // ignore upper bytes not fitting in long
}
else
{
// big endian (sign extended)*/
val = (signed char) input[length++];
// big endian */
if (format.flags & zero_flag)
{
// fill with zero
val = (unsigned char) input[length++];
}
else
{
// fill with sign
val = (signed char) input[length++];
}
while (--width)
{
val <<= 8;

125
src/RegexpConverter.cc Normal file
View File

@ -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, "/");

View File

@ -60,7 +60,8 @@ grow(long minsize)
// make space for minsize + 1 (for termination) bytes
char* newbuffer;
long newcap;
if (minsize > 10000)
#ifdef EXPLODE
if (minsize > 1000000)
{
// crude trap against infinite grow
error ("StreamBuffer exploded growing from %ld to %ld chars. Exiting\n",
@ -83,6 +84,7 @@ grow(long minsize)
fprintf(stderr, "\n");
abort();
}
#endif
if (minsize < cap)
{
// just move contents to start of buffer and clear end

View File

@ -89,7 +89,7 @@ connectRequest (unsigned long)
}
bool StreamBusInterface::
disconnect ()
disconnectRequest ()
{
return false;
}
@ -137,6 +137,11 @@ connectCallback(StreamIoStatus)
{
}
void StreamBusInterface::Client::
disconnectCallback(StreamIoStatus)
{
}
long StreamBusInterface::Client::
priority()
{

View File

@ -42,6 +42,7 @@ public:
const void* input, long size);
virtual void eventCallback(StreamIoStatus status);
virtual void connectCallback(StreamIoStatus status);
virtual void disconnectCallback(StreamIoStatus status);
virtual long priority();
virtual const char* name() = 0;
virtual const char* getInTerminator(size_t& length) = 0;
@ -86,7 +87,7 @@ public:
return businterface->connectRequest(timeout_ms);
}
bool busDisconnect() {
return businterface->disconnect();
return businterface->disconnectRequest();
}
};
@ -113,6 +114,8 @@ protected:
{ client->eventCallback(status); }
void connectCallback(StreamIoStatus status)
{ client->connectCallback(status); }
void disconnectCallback(StreamIoStatus status)
{ client->disconnectCallback(status); }
const char* getInTerminator(size_t& length)
{ return client->getInTerminator(length); }
const char* getOutTerminator(size_t& length)
@ -132,7 +135,7 @@ protected:
unsigned long replytimeout_ms); // supportsEvents() returns true
virtual void release();
virtual bool connectRequest(unsigned long connecttimeout_ms);
virtual bool disconnect();
virtual bool disconnectRequest();
virtual void finish();
// pure virtual

View File

@ -23,6 +23,12 @@
#include <ctype.h>
#include <stdlib.h>
#include <epicsExport.h>
int showAsyncErrors = 0;
extern "C" {
epicsExportAddress(int, showAsyncErrors);
}
enum Commands { end_cmd, in_cmd, out_cmd, wait_cmd, event_cmd, exec_cmd,
connect_cmd, disconnect_cmd };
const char* commandStr[] = { "end", "in", "out", "wait", "event", "exec",
@ -30,7 +36,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 /////////////////////////////////////////////
@ -801,6 +807,8 @@ writeCallback(StreamIoStatus status)
flags &= ~WritePending;
if (status != StreamIoSuccess)
{
error("%s: write failed: %s\n",
name(), StreamIoStatusStr[status]);
finishProtocol(WriteTimeout);
return;
}
@ -921,7 +929,11 @@ readCallback(StreamIoStatus status,
finishProtocol(ReplyTimeout);
return 0;
case StreamIoFault:
error("%s: I/O error when reading from device\n", name());
error("%s: I/O error after reading %ld byte%s: \"%s%s\"\n",
name(),
inputBuffer.length(), inputBuffer.length()==1 ? "" : "s",
inputBuffer.length() > 20 ? "..." : "",
inputBuffer.expand(-20,20)());
finishProtocol(Fault);
return 0;
}
@ -985,7 +997,7 @@ readCallback(StreamIoStatus status,
}
// try to parse what we got
end = inputBuffer.length();
if (!(flags & AsyncMode))
if (!(flags & AsyncMode)||showAsyncErrors)
{
error("%s: Timeout after reading %ld byte%s \"%s%s\"\n",
name(), end, end==1 ? "" : "s", end > 20 ? "..." : "",
@ -1115,7 +1127,7 @@ matchInput()
}
if (consumed < 0)
{
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
{
error("%s: Input \"%s%s\" does not match format %%%s\n",
name(), inputLine.expand(consumedInput, 20)(),
@ -1130,7 +1142,7 @@ matchInput()
flags &= ~Separator;
if (!matchValue(fmt, fieldAddress ? fieldAddress() : NULL))
{
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
{
if (flags & ScanTried)
error("%s: Input \"%s%s\" does not match format %%%s\n",
@ -1160,7 +1172,7 @@ matchInput()
{
int i = 0;
while (commandIndex[i] >= ' ') i++;
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
{
error("%s: Input \"%s%s\" too short."
" No match for \"%s\"\n",
@ -1173,7 +1185,7 @@ matchInput()
}
if (command != inputLine[consumedInput])
{
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
{
int i = 0;
while (commandIndex[i] >= ' ') i++;
@ -1198,7 +1210,7 @@ matchInput()
long surplus = inputLine.length()-consumedInput;
if (surplus > 0 && !(flags & IgnoreExtraInput))
{
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
{
error("%s: %ld byte%s surplus input \"%s%s\"\n",
name(), surplus, surplus==1 ? "" : "s",
@ -1323,7 +1335,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;
@ -1510,8 +1522,23 @@ bool StreamCore::evalDisconnect()
finishProtocol(Fault);
return false;
}
evalCommand();
return true;
}
void StreamCore::
disconnectCallback(StreamIoStatus status)
{
switch (status)
{
case StreamIoSuccess:
evalCommand();
return;
default:
error("%s: Disconnect failed\n",
name());
finishProtocol(Fault);
return;
}
}
#include "streamReferences"

View File

@ -200,6 +200,7 @@ protected:
void eventCallback(StreamIoStatus status);
void execCallback(StreamIoStatus status);
void connectCallback(StreamIoStatus status);
void disconnectCallback(StreamIoStatus status);
const char* getInTerminator(size_t& length);
const char* getOutTerminator(size_t& length);

View File

@ -1180,11 +1180,11 @@ void streamExecuteCommand(CALLBACK *pcallback)
if (execute(pstream->outputLine()) != OK)
{
pstream->execCallback(StreamBusInterface::ioFault);
pstream->execCallback(StreamIoFault);
}
else
{
pstream->execCallback(StreamBusInterface::ioSuccess);
pstream->execCallback(StreamIoSuccess);
}
}
#endif

View File

@ -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++;

View File

@ -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;

View File

@ -22,7 +22,7 @@
#define devStream_h
#define STREAM_MAJOR 2
#define STREAM_MINOR 2
#define STREAM_MINOR 3
#if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h>

View File

@ -20,7 +20,7 @@
#include <string.h>
#include <stdlib.h>
#include <devStream.h>
#include "devStream.h"
#include <aaiRecord.h>
#include <epicsExport.h>

View File

@ -20,7 +20,7 @@
#include <string.h>
#include <stdlib.h>
#include <devStream.h>
#include "devStream.h"
#include <aaoRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <aiRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <aoRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <biRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <boRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -17,7 +17,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <postfix.h>
#include <calcoutRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <longinRecord.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <longoutRecord.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbbiDirectRecord.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbbiRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbboDirectRecord.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbboRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <stringinRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <stringoutRecord.h>
#include <epicsExport.h>

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <waveformRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -1,204 +0,0 @@
/***************************************************************
* memguard *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* Include memguard.h and link memguard.o to get some memory *
* guardiance. Allocated memory (using new) is tracked with *
* source file and line of the new operator. Deleted memory is *
* checked for duplicate deletion. *
* Call memguardReport() to see all allocated memory. *
* Calling memguardReport() shows currently allocated memory. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#undef new
#undef delete
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct memheader
{
static memheader* first;
memheader* next;
size_t size;
long line;
const char* file;
long fill1;
long fill2;
unsigned long magic;
};
extern "C" void memguardReport();
class memguard
{
public:
memguard() {atexit(memguardReport);}
void report();
};
memheader* memheader::first = NULL;
static memguard guard;
extern "C" void memguardReport()
{
guard.report();
}
void* operator new(size_t size, const char* file, long line) throw (std::bad_alloc)
{
memheader* header;
memheader** prev;
header = (memheader*) malloc(size + sizeof(memheader));
if (!header)
{
fprintf (stderr, "out of memory\n");
if (file) fprintf (stderr, "in %s line %ld\n", file, line);
guard.report();
abort();
}
header->magic = 0xA110caed;
for (prev = &memheader::first; *prev; prev = &(*prev)->next);
*prev = header;
header->next = NULL;
header->size = size;
header->line = line;
header->file = file;
// fprintf(stderr, "allocating %p: %d bytes for %s line %ld\n",
// header+1, size, file, line);
return header+1;
}
void* operator new[](size_t size, const char* file, long line) throw (std::bad_alloc)
{
return operator new(size, file, line);
}
void* operator new(size_t size) throw (std::bad_alloc)
{
return operator new(size, NULL, 0);
}
void* operator new[](size_t size) throw (std::bad_alloc)
{
return operator new(size, NULL, 0);
}
static const char* deleteFile = NULL;
static long deleteLine = 0;
int memguardLocation(const char* file, long line)
{
deleteFile = file;
deleteLine = line;
return 1;
}
void operator delete (void* memory) throw ()
{
memheader* header;
memheader** prev;
if (!memory)
{
deleteFile = NULL;
deleteLine = 0;
return;
}
header = ((memheader*)memory)-1;
if (header->magic == 0xDeadBeef)
{
fprintf (stderr, "memory at %p deleted twice\n", memory);
if (header->file)
fprintf (stderr, "allocated by %s line %ld\n",
header->file, header->line);
guard.report();
abort();
}
for (prev = &memheader::first; *prev; prev = &(*prev)->next)
{
if (*prev == header)
{
if (header->magic != 0xA110caed)
{
fprintf (stderr, "deleted memory at %p corrupt\n", memory);
if (header->file)
fprintf (stderr, "allocated by %s line %ld\n",
header->file, header->line);
guard.report();
abort();
}
*prev = header->next;
header->magic = 0xDeadBeef;
// fprintf(stderr, "deleting %p ", memory);
// if (header->file) fprintf(stderr, "allocated in %s line %ld\n",
// header->file, header->line);
// if (deleteFile) fprintf(stderr, "probably deleted in %s line %ld\n",
// deleteFile, deleteLine);
deleteFile = NULL;
deleteLine = 0;
free(header);
// fprintf(stderr, "done\n");
return;
}
}
if (deleteFile)
{
fprintf (stderr, "deleted memory at %p was never allocated\n", memory);
fprintf (stderr, "delete was probably called in %s line %ld\n",
deleteFile, deleteLine);
abort();
}
}
void operator delete[](void* memory) throw ()
{
operator delete(memory);
}
void memguard::report()
{
memheader* header;
unsigned long sum = 0;
unsigned long chunks = 0;
if (memheader::first)
{
fprintf(stderr, "allocated memory:\n");
for (header = memheader::first; header; header = header->next)
{
chunks++;
sum += header->size;
if (header->file)
{
fprintf (stderr, "%p: %d bytes by %s line %ld\n",
header+1, header->size, header->file, header->line);
}
else
{
fprintf (stderr, "%p: %d bytes by unknown\n",
header+1, header->size);
}
if (header->magic != 0xA110caed)
{
fprintf (stderr, "memory list is corrupt\n");
abort();
}
}
fprintf (stderr, "%lu bytes in %lu chunks\n", sum, chunks);
}
else
{
fprintf(stderr, "no memory allocated\n");
}
}

View File

@ -1,30 +0,0 @@
/***************************************************************
* memguard *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* Include memguard.h and link memguard.o to get some memory *
* guardiance. Allocated memory (using new) is tracked with *
* source file and line of the new operator. Deleted memory is *
* checked for duplicate deletion. *
* Call memguardReport() to see all allocated memory. *
* Calling memguardReport() shows currently allocated memory. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifdef __cplusplus
#ifndef memguard_h
#define memguard_h
#include <new.h>
#define MEMGUARD
void* operator new(size_t, const char*, long) throw (std::bad_alloc);
void* operator new[](size_t, const char*, long) throw (std::bad_alloc);
#define new new(__FILE__,__LINE__)
int memguardLocation(const char*, long);
#define delete if (memguardLocation(__FILE__,__LINE__)) delete
extern "C" void memguardReport();
#endif
#endif