Compare commits
42 Commits
stream_2_7
...
2-7
Author | SHA1 | Date | |
---|---|---|---|
d5dc15e321 | |||
cfa777d718 | |||
54bc78f7c7 | |||
77d110de70 | |||
19467bd5d5 | |||
d69c74bc8f | |||
e09506c2bb | |||
343eef324b | |||
a798cf2f21 | |||
23f3e806e2 | |||
56b6c9a627 | |||
2830f07324 | |||
abd8daafc3 | |||
489e783872 | |||
10d1fa8b02 | |||
c8bffebfc6 | |||
0a90eb3d9c | |||
0936ac7840 | |||
704ece6231 | |||
5c6e98127e | |||
8805437c68 | |||
3acf791409 | |||
0ba674a341 | |||
40c33abac7 | |||
bc67317b0b | |||
06e212c66e | |||
7b314ccffd | |||
1d84986ee8 | |||
2ca8a129f7 | |||
126da8c499 | |||
ea110d5047 | |||
b37fad41c6 | |||
74775996db | |||
74426aab66 | |||
690bbb13d4 | |||
871fbed2b0 | |||
85c68d2ae6 | |||
8870611d4d | |||
67af7fc1bf | |||
93bea174e4 | |||
84fc6aabc8 | |||
38c4f5bcb6 |
@ -1,3 +1,4 @@
|
|||||||
|
# Remove this file if not using the PSI build system
|
||||||
include /ioc/tools/driver.makefile
|
include /ioc/tools/driver.makefile
|
||||||
EXCLUDE_VERSIONS = 3.13.2
|
EXCLUDE_VERSIONS = 3.13.2
|
||||||
PROJECT=stream
|
PROJECT=stream
|
||||||
@ -39,20 +40,17 @@ HEADERS += StreamFormatConverter.h
|
|||||||
HEADERS += StreamBuffer.h
|
HEADERS += StreamBuffer.h
|
||||||
HEADERS += StreamError.h
|
HEADERS += StreamError.h
|
||||||
|
|
||||||
ifeq (${EPICS_BASETYPE},3.13)
|
ifneq (${EPICS_BASETYPE},3.13)
|
||||||
# old gcc needs full path for -include
|
|
||||||
CXXFLAGS += -include $(foreach d,${INCLUDES:-I%=%},$(wildcard $d/compat3_13.h))
|
|
||||||
else
|
|
||||||
RECORDTYPES += calcout
|
RECORDTYPES += calcout
|
||||||
endif
|
endif
|
||||||
|
|
||||||
StreamCore.o: streamReferences
|
StreamCore.o StreamCore.d: streamReferences
|
||||||
|
|
||||||
streamReferences:
|
streamReferences:
|
||||||
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
||||||
perl ../src/makeref.pl Converter $(FORMATS) >> $@
|
perl ../src/makeref.pl Converter $(FORMATS) >> $@
|
||||||
|
|
||||||
# have to hack a bit to work with both versions of driver.makefile
|
export DBDFILES = streamSup.dbd
|
||||||
DBDFILES = O.$${EPICSVERSION}_$${T_A}/streamSup.dbd
|
streamSup.dbd:
|
||||||
../O.${EPICSVERSION}_${T_A}/streamSup.dbd:
|
@echo Creating $@
|
||||||
perl ../src/makedbd.pl $(RECORDTYPES) > $@
|
perl ../src/makedbd.pl $(RECORDTYPES) > $@
|
5
MODULE
5
MODULE
@ -1,5 +0,0 @@
|
|||||||
# Please change the following email with yours.
|
|
||||||
Email: dirk.zimoch@psi.ch
|
|
||||||
Module-Name: StreamDevice2
|
|
||||||
Description: StreamDevice2
|
|
||||||
Project-Name:
|
|
@ -41,7 +41,7 @@ written or read value.
|
|||||||
(=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
|
(=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
|
||||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||||
on 64 bit machines.
|
on 64 bit machines.
|
||||||
<p class="new">
|
<p>
|
||||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>VAL</code>
|
If <code>LINR=="NO CONVERSION"</code> (the default), <code>VAL</code>
|
||||||
is directly converted from and to <code>long</code> without going through
|
is directly converted from and to <code>long</code> without going through
|
||||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||||
@ -84,6 +84,6 @@ written or read value.
|
|||||||
<a href="calcout.html">calcout</a>
|
<a href="calcout.html">calcout</a>
|
||||||
<a href="scalcout.html">scalcout</a>
|
<a href="scalcout.html">scalcout</a>
|
||||||
</p>
|
</p>
|
||||||
<p><small>Dirk Zimoch, 2005</small></p>
|
<p><small>Dirk Zimoch, 2015</small></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -40,7 +40,7 @@ written or read value.
|
|||||||
(=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
|
(=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
|
||||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||||
on 64 bit machines.
|
on 64 bit machines.
|
||||||
<p class="new">
|
<p>
|
||||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>OVAL</code>
|
If <code>LINR=="NO CONVERSION"</code> (the default), <code>OVAL</code>
|
||||||
is directly converted to <code>long</code> without going through
|
is directly converted to <code>long</code> without going through
|
||||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||||
@ -85,6 +85,6 @@ written or read value.
|
|||||||
<a href="calcout.html">calcout</a>
|
<a href="calcout.html">calcout</a>
|
||||||
<a href="scalcout.html">scalcout</a>
|
<a href="scalcout.html">scalcout</a>
|
||||||
</p>
|
</p>
|
||||||
<p><small>Dirk Zimoch, 2005</small></p>
|
<p><small>Dirk Zimoch, 2015</small></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
189
doc/formats.html
189
doc/formats.html
@ -28,8 +28,8 @@ A format converter consists of
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The <code>%</code> character</li>
|
<li>The <code>%</code> character</li>
|
||||||
<li>Optionally a field <span class="new">or record</span> name in <code>()</code></li>
|
<li>Optionally a field or record name in <code>()</code></li>
|
||||||
<li>Optionally flags out of the characters <code>*# +0-<span class="new">?=</span></code></li>
|
<li>Optionally flags out of the characters <code>*# +0-?=!</code></li>
|
||||||
<li>Optionally an integer <em>width</em> field</li>
|
<li>Optionally an integer <em>width</em> field</li>
|
||||||
<li>Optionally a period character (<code>.</code>) followed
|
<li>Optionally a period character (<code>.</code>) followed
|
||||||
by an integer <em>precision</em> field (input ony for most formats)</li>
|
by an integer <em>precision</em> field (input ony for most formats)</li>
|
||||||
@ -40,7 +40,7 @@ A format converter consists of
|
|||||||
<p>
|
<p>
|
||||||
The flags <code>*# +0-</code> work like in the C functions
|
The flags <code>*# +0-</code> work like in the C functions
|
||||||
<em>printf()</em> and <em>scanf()</em>.
|
<em>printf()</em> and <em>scanf()</em>.
|
||||||
The flags <code>?</code> and <code>=</code> are extensions.
|
The flags <code>?</code>, <code>=</code> and <code>!</code> are extensions.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The <code>*</code> flag skips data in input formats.
|
The <code>*</code> flag skips data in input formats.
|
||||||
@ -67,16 +67,24 @@ The <code>0</code> flag says that numbers should be left padded with
|
|||||||
The <code>-</code> flag specifies that output is left justified if
|
The <code>-</code> flag specifies that output is left justified if
|
||||||
<em>width</em> is larger than required.
|
<em>width</em> is larger than required.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
The <code>?</code> flag makes failing input conversions succeed with
|
The <code>?</code> flag makes failing input conversions succeed with
|
||||||
a default zero value (0, 0.0, or "", depending on the format type).
|
a default zero value (0, 0.0, or "", depending on the format type).
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
The <code>=</code> flag allows to compare input with current values.
|
The <code>=</code> flag allows to compare input with current values.
|
||||||
It is only allowed in input formats.
|
It is only allowed in input formats.
|
||||||
Instead of reading a new value from input, the current value is
|
Instead of reading a new value from input, the current value is
|
||||||
formatted (like for output) and then compared to the input.
|
formatted (like for output) and then compared to the input.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
The <code>!</code> flag demands that input is exactly <em>width</em>
|
||||||
|
bytes long (normally <em>width</em> defines the maximum number of
|
||||||
|
bytes read in many formats).
|
||||||
|
For example <code>in "%!5d";</code> expects exactly 5 digits.
|
||||||
|
Fewer digits are considered loss of data and make the format fail.
|
||||||
|
This feature has been added by Klemen Vodopivec, SNS.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Examples:</h3>
|
<h3>Examples:</h3>
|
||||||
<table>
|
<table>
|
||||||
@ -143,11 +151,8 @@ field formatted as a string.
|
|||||||
Use <code>in "%(<i>otherrecord</i>.RVAL)f";</code> to write the floating
|
Use <code>in "%(<i>otherrecord</i>.RVAL)f";</code> to write the floating
|
||||||
point input value into the <code>RVAL</code> field of
|
point input value into the <code>RVAL</code> field of
|
||||||
<code><i>otherrecord</i></code>.
|
<code><i>otherrecord</i></code>.
|
||||||
<span class="new">
|
|
||||||
If no field is given for an other record .VAL is assumed.
|
If no field is given for an other record .VAL is assumed.
|
||||||
When a record name conflicts with a field name use .VAL explicitly.
|
When a record name conflicts with a field name use .VAL explicitly.
|
||||||
</span>
|
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This feature is very useful when one line of input contains many values that should
|
This feature is very useful when one line of input contains many values that should
|
||||||
@ -158,7 +163,7 @@ attribute (see
|
|||||||
target="ex">Record Reference Manual</a>), the record will be processed.
|
target="ex">Record Reference Manual</a>), the record will be processed.
|
||||||
It is your responsibility that the data type of the record field is
|
It is your responsibility that the data type of the record field is
|
||||||
compatible to the the data type of the converter.
|
compatible to the the data type of the converter.
|
||||||
<span class="new">STRING formats are compatible with arrays of CHAR or UCHAR.<span>
|
STRING formats are compatible with arrays of CHAR or UCHAR.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Note that using this syntax is by far not as efficient as using the
|
Note that using this syntax is by far not as efficient as using the
|
||||||
@ -192,11 +197,11 @@ With the <code>#</code> flag, output always contains a period character.
|
|||||||
<p>
|
<p>
|
||||||
<b>Input:</b> All these formats are equivalent. Leading whitespaces are skipped.
|
<b>Input:</b> All these formats are equivalent. Leading whitespaces are skipped.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
With the <code>#</code> flag additional whitespace between sign and number
|
With the <code>#</code> flag additional whitespace between sign and number
|
||||||
is accepted.
|
is accepted.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
When a maximum field width is given, leading whitespace only counts to the
|
When a maximum field width is given, leading whitespace only counts to the
|
||||||
field witdth when the space flag is used.
|
field witdth when the space flag is used.
|
||||||
</p>
|
</p>
|
||||||
@ -213,7 +218,7 @@ field witdth when the space flag is used.
|
|||||||
<p>
|
<p>
|
||||||
With the <code>#</code> flag, octal values are prefixed with <code>0</code>
|
With the <code>#</code> flag, octal values are prefixed with <code>0</code>
|
||||||
and hexadecimal values with <code>0x</code> or <code>0X</code>.
|
and hexadecimal values with <code>0x</code> or <code>0X</code>.
|
||||||
<p class="new">
|
<p>
|
||||||
Unlike printf, <code>%x</code> and <code>%X</code> truncate the
|
Unlike printf, <code>%x</code> and <code>%X</code> truncate the
|
||||||
output to the the given width (number of least significant half bytes).
|
output to the the given width (number of least significant half bytes).
|
||||||
</p>
|
</p>
|
||||||
@ -228,14 +233,14 @@ Octal and hexadecimal values can optionally be prefixed.
|
|||||||
hexadecimal notation.
|
hexadecimal notation.
|
||||||
Leading whitespaces are skipped.
|
Leading whitespaces are skipped.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
With the <code>-</code> negative octal and hexadecimal values are accepted.
|
With the <code>-</code> negative octal and hexadecimal values are accepted.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
With the <code>#</code> flag additional whitespace between sign and number
|
With the <code>#</code> flag additional whitespace between sign and number
|
||||||
is accepted.
|
is accepted.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
When a maximum field width is given, leading whitespace only counts to the
|
When a maximum field width is given, leading whitespace only counts to the
|
||||||
field witdth when the space flag is used.
|
field witdth when the space flag is used.
|
||||||
</p>
|
</p>
|
||||||
@ -253,13 +258,11 @@ and <code>%c</code> matches a sequence of not-null characters.
|
|||||||
The maximum string length is given by <em>width</em>.
|
The maximum string length is given by <em>width</em>.
|
||||||
The default <em>width</em> is infinite for <code>%s</code> and
|
The default <em>width</em> is infinite for <code>%s</code> and
|
||||||
1 for <code>%c</code>.
|
1 for <code>%c</code>.
|
||||||
Leading whitespaces are skipped with <code>%s</code>
|
Leading whitespaces are skipped with <code>%s</code> except when
|
||||||
<span class="new">
|
the space flag is used but not with <code>%c</code>.
|
||||||
except when the space flag is used</span>
|
|
||||||
but not with <code>%c</code>.
|
|
||||||
The empty string matches.
|
The empty string matches.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
With the <code>#</code> flag <code>%s</code> matches a sequence of not-null
|
With the <code>#</code> flag <code>%s</code> matches a sequence of not-null
|
||||||
characters instead of non-whitespace characters.
|
characters instead of non-whitespace characters.
|
||||||
</p>
|
</p>
|
||||||
@ -290,18 +293,18 @@ The strings are separated by <code>|</code>.
|
|||||||
Example: <code>%{OFF|STANDBY|ON}</code> mapps the string <code>OFF</code>
|
Example: <code>%{OFF|STANDBY|ON}</code> mapps the string <code>OFF</code>
|
||||||
to the value 0, <code>STANDBY</code> to 1 and <code>ON</code> to 2.
|
to the value 0, <code>STANDBY</code> to 1 and <code>ON</code> to 2.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
When using the <code>#</code> flag it is allowed to assign integer values
|
When using the <code>#</code> flag it is allowed to assign integer values
|
||||||
to the strings using <code>=</code>.
|
to the strings using <code>=</code>.
|
||||||
Unassigned strings increment their values by 1 as usual.
|
Unassigned strings increment their values by 1 as usual.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
If one string is the initial substing of another, the substing must come
|
If one string is the initial substing of another, the substing must come
|
||||||
later to ensure correct matching.
|
later to ensure correct matching.
|
||||||
In particular if one string is the emptry string, it must be the last one.
|
In particular if one string is the emptry string, it must be the last one.
|
||||||
Use <code>#</code> and <code>=</code> to renumber if necessary.
|
Use <code>#</code> and <code>=</code> to renumber if necessary.
|
||||||
</p>
|
</p>
|
||||||
<p class="new">
|
<p>
|
||||||
Use the assignment <code>=?</code> for the last string to make it the
|
Use the assignment <code>=?</code> for the last string to make it the
|
||||||
default value for output formats.
|
default value for output formats.
|
||||||
</p>
|
</p>
|
||||||
@ -310,12 +313,12 @@ Example: <code>%#{neg=-1|stop|pos|fast=10|rewind=-10}</code>.
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If one of the strings contains <code>|</code> or <code>}</code>
|
If one of the strings contains <code>|</code> or <code>}</code>
|
||||||
<span class="new">(or <code>=</code> if the <code>#</code> flag is used)</span>
|
(or <code>=</code> if the <code>#</code> flag is used)
|
||||||
a <code>\</code> must be used to escape the character.
|
a <code>\</code> must be used to escape the character.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Output:</b> Depending on the value, one of the strings is printed,
|
<b>Output:</b> Depending on the value, one of the strings is printed,
|
||||||
<span class="new">or the default if no value matches</span>.
|
or the default if no value matches.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Input:</b> If any of the strings matches, the value is set accordingly.
|
<b>Input:</b> If any of the strings matches, the value is set accordingly.
|
||||||
@ -364,9 +367,12 @@ endian</em>, i.e. least significant byte first.
|
|||||||
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In output, the <em>prec</em> (or sizeof(long) whatever is less) least
|
In output, the <em>precision</em> (or sizeof(long) whatever is less) least
|
||||||
significant bytes of the value are sign extended or zero extended
|
significant bytes of the value are sign extended or zero extended
|
||||||
(depending on the <code>0</code> flag) to <em>width</em> bytes.
|
(depending on the <code>0</code> flag) to <em>width</em> bytes.
|
||||||
|
The default for <em>precision</em> is 1. Thus if you do not specify
|
||||||
|
the <em>precision</em>, only the least significant byte is written!
|
||||||
|
It is common error to write <code>out "%2r";</code> instead of <code>out "%.2r";</code>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In input, <em>width</em> bytes are read and put into the value.
|
In input, <em>width</em> bytes are read and put into the value.
|
||||||
@ -377,7 +383,7 @@ the value is sign extended or zero extended, depending on the
|
|||||||
<code>0</code> flag.
|
<code>0</code> flag.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Example: <code>out "%.2r"</code>
|
Examples: <code>out "%.2r"; in "%02r";</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="rawdouble"></a>
|
<a name="rawdouble"></a>
|
||||||
@ -431,7 +437,7 @@ The <em>width</em> field is the byte number from which to start
|
|||||||
calculating the checksum.
|
calculating the checksum.
|
||||||
Default is 0, i.e. the first byte of the input or output of the current
|
Default is 0, i.e. the first byte of the input or output of the current
|
||||||
command.
|
command.
|
||||||
The last byte is <em>prec</em> bytes before the checksum (default 0).
|
The last byte is <em>precision</em> bytes before the checksum (default 0).
|
||||||
For example in <code>"abcdefg%<xor>"</code> the checksum is calculated
|
For example in <code>"abcdefg%<xor>"</code> the checksum is calculated
|
||||||
from <code>abcdefg</code>,
|
from <code>abcdefg</code>,
|
||||||
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
||||||
@ -485,21 +491,24 @@ In input, the next byte or bytes must match the checksum.
|
|||||||
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
||||||
<dt><code>%<ccitt8></code></dt>
|
<dt><code>%<ccitt8></code></dt>
|
||||||
<dd>One byte. The CCITT standard 8 bit crc checksum
|
<dd>One byte. The CCITT standard 8 bit crc checksum
|
||||||
(poly=0x31, init=0x00, xorout=0x00).</dd>
|
(poly=0x31, init=0x00, xorout=0x00, reflected).</dd>
|
||||||
<dt><code>%<crc16></code></dt>
|
<dt><code>%<crc16></code></dt>
|
||||||
<dd>Two bytes. An often used 16 bit crc checksum
|
<dd>Two bytes. An often used 16 bit crc checksum
|
||||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||||
<dt><code>%<crc16r></code></dt>
|
<dt><code>%<crc16r></code></dt>
|
||||||
<dd>Two bytes. An often used reflected 16 bit crc checksum
|
<dd>Two bytes. An often used reflected 16 bit crc checksum
|
||||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
(poly=0x8005, init=0x0000, xorout=0x0000, reflected).</dd>
|
||||||
|
<dt><code>%<modbus></code></dt>
|
||||||
|
<dd>Two bytes. The modbus 16 bit crc checksum
|
||||||
|
(poly=0x8005, init=0xffff, xorout=0x0000, reflected)</dd>
|
||||||
<dt><code>%<ccitt16></code></dt>
|
<dt><code>%<ccitt16></code></dt>
|
||||||
<dd>Two bytes. The usual (but <a target="ex"
|
<dd>Two bytes. The usual (but <a target="ex"
|
||||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
|
href="http://srecord.sourceforge.net/crc16-ccitt.html">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>
|
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
|
||||||
<dt><code>%<ccitt16a></code></dt>
|
<dt><code>%<ccitt16a></code></dt>
|
||||||
<dd>Two bytes. The unusual (but <a target="ex"
|
<dd>Two bytes. The unusual (but <a target="ex"
|
||||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
|
href="http://srecord.sourceforge.net/crc16-ccitt.html">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>
|
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
|
||||||
<dt><code>%<ccitt16x></code> or <code>%<crc16c></code> or <code>%<xmodem></code></dt>
|
<dt><code>%<ccitt16x></code> or <code>%<crc16c></code> or <code>%<xmodem></code></dt>
|
||||||
@ -510,10 +519,10 @@ In input, the next byte or bytes must match the checksum.
|
|||||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||||
<dt><code>%<crc32r></code></dt>
|
<dt><code>%<crc32r></code></dt>
|
||||||
<dd>Four bytes. The standard reflected 32 bit crc checksum.
|
<dd>Four bytes. The standard reflected 32 bit crc checksum.
|
||||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF, reflected).</dd>
|
||||||
<dt><code>%<jamcrc></code></dt>
|
<dt><code>%<jamcrc></code></dt>
|
||||||
<dd>Four bytes. Another reflected 32 bit crc checksum.
|
<dd>Four bytes. Another reflected 32 bit crc checksum.
|
||||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
|
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000, reflected).</dd>
|
||||||
<dt><code>%<adler32></code></dt>
|
<dt><code>%<adler32></code></dt>
|
||||||
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
||||||
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
||||||
@ -522,41 +531,44 @@ In input, the next byte or bytes must match the checksum.
|
|||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<a name="regex"></a>
|
<a name="regex"></a>
|
||||||
<h2>12. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
<h2>13. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This input-only format matches <a target="ex"
|
This input-only format matches <a target="ex"
|
||||||
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
||||||
It is only available if a PCRE library is installed.
|
It is only available if a PCRE library is installed.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="box">
|
||||||
<p>
|
<p>
|
||||||
If PCRE is not available for your host or cross architecture, download
|
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>
|
the sourcecode from <a target="ex" href="http://www.pcre.org/">www.pcre.org</a>
|
||||||
and try my EPICS compatible <a target="ex"
|
and try my EPICS compatible <a target="ex"
|
||||||
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
||||||
to compile it like a normal EPICS application.
|
to compile it like a normal EPICS support module.
|
||||||
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
|
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
|
In your RELEASE file define the variable <code>PCRE</code> so that
|
||||||
it points to the install location of PCRE.
|
it points to the install location of PCRE.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If PCRE is already installed on your system, use the variables
|
If PCRE is already installed on (some of) your systems, you may add
|
||||||
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
|
architectures where PCRE can be found in standard include and library
|
||||||
the install directories of <code>pcre.h</code> and the library.
|
locations to the variable <code>WITH_SYSTEM_PCRE</code>.
|
||||||
</p>
|
If either the header file or the library are in a non-standard place,
|
||||||
<p>
|
set in your RELEASE file the variables <code>PCRE_INCLUDE_<em>arch</em></code>
|
||||||
If you have PCRE installed in different locations for different (cross)
|
and/or <code>PCRE_LIB_<em>arch</em></code> for the respective architectures
|
||||||
architectures, define the variables in RELEASE.Common.<architecture>
|
to the correct directories or set
|
||||||
instead of the global RELEASE file.
|
<code>PCRE_INCLUDE</code> and/or <code>PCRE_LIB</code>
|
||||||
|
in architecture specific RELEASE.Common.<em>arch</em> files.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
<p>
|
<p>
|
||||||
If the regular expression is not anchored, i.e. does not start with
|
If the regular expression is not anchored, i.e. does not start with
|
||||||
<code>^</code>, leading non-matching input is skipped.
|
<code>^</code>, leading non-matching input is skipped.
|
||||||
A maximum of <em>width</em> bytes is matched, if specified.
|
A maximum of <em>width</em> bytes is matched, if specified.
|
||||||
If <em>prec</em> is given, it specifies the sub-expression whose match
|
If <em>precision</em> is given, it specifies the sub-expression in <code>()</code>
|
||||||
is retuned.
|
whose match is retuned.
|
||||||
Otherwise the complete match is returned.
|
Otherwise the complete match is returned.
|
||||||
In any case, the complete match is consumed from the input buffer.
|
In any case, the complete match is consumed from the input buffer.
|
||||||
If the expression contains a <code>/</code> it must be escaped.
|
If the expression contains a <code>/</code> it must be escaped like <code>\/</code>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
||||||
@ -564,8 +576,81 @@ the title of an HTML page, skipps anything before the
|
|||||||
<code><title></code> tag and leaves anything after the
|
<code><title></code> tag and leaves anything after the
|
||||||
<code></title></code> tag in the input buffer.
|
<code></title></code> tag in the input buffer.
|
||||||
</p>
|
</p>
|
||||||
|
<a name="regsub"></a>
|
||||||
|
<h2>14. Regular Expresion Substitution Pseudo-Converter (<code>%#/<em>regex</em>/<em>subst</em>/</code>)</h2>
|
||||||
|
<p>
|
||||||
|
This is a variant of the previous converter (note the <code>#</code>)
|
||||||
|
but instead of returning the matching string,
|
||||||
|
it can be used as a pre-processor for input or
|
||||||
|
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> 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>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If <em>width</em> is specified, it limits the number of characters processed.
|
||||||
|
If the <code>-</code> flag is used (i.e. <em>width</em> looks like a negative number)
|
||||||
|
only the last <em>width</em> characters are processed, else the first.
|
||||||
|
Without <em>width</em> (or 0) all available characters are processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If <em>precision</em> is specified, it indicates which matches to replace.
|
||||||
|
With the <code>+</code> flag given, <em>precision</em> is the maximum
|
||||||
|
number of matches to replace.
|
||||||
|
Otherwise <em>precision</em> is the index (counting from 1) of the match to replace.
|
||||||
|
Without <em>precision</em> (or 0), all matches are replaced.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In input this converter pre-processes data received from the device before
|
||||||
|
following converters read it.
|
||||||
|
Converters preceding this one will read unmodified input.
|
||||||
|
Thus place this converter before those whose input should be pre-processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In output it post-processes data already formatted by preceding converters
|
||||||
|
before sending it to the device.
|
||||||
|
Converters following this one will send their output unmodified.
|
||||||
|
Thus place this converter after those whose output should be post-processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Examples:
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#+-10.2/ab/X/</code> replaces the string <code>ab</code> with <code>X</code>
|
||||||
|
maximal 2 times in the last 10 characters.
|
||||||
|
(<code>abcabcabcabc</code> becomes <code>abcXcXcabc</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/\\/\//</code> replaces all <code>\</code> with <code>/</code>
|
||||||
|
(<code>\dir\file</code> becomes <code>/dir/file</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/..\B/&:/</code> inserts <code>:</code> after every second character
|
||||||
|
which is not at the end of a word.
|
||||||
|
(<code>0b19353134</code> becomes <code>0b:19:35:31:34</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/://</code> removes all <code>:</code> characters.
|
||||||
|
(<code>0b:19:35:31:34</code> becomes <code>0b19353134</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/([^+-])*([+-])/\2\1/</code> moves a postfix sign to the front.
|
||||||
|
(<code>1.23-</code> becomes <code>-1.23</code>)<br>
|
||||||
|
</div>
|
||||||
<a name="mantexp"></a>
|
<a name="mantexp"></a>
|
||||||
<h2>13. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
<h2>15. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This exotic and experimental format matches numbers in the format
|
This exotic and experimental format matches numbers in the format
|
||||||
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
||||||
@ -584,9 +669,8 @@ Format flags <code>+</code>, <code>-</code>, and space are supported in
|
|||||||
the usual way (always sign, left justified, space instead of + sign).
|
the usual way (always sign, left justified, space instead of + sign).
|
||||||
Flags <code>#</code> and <code>0</code> are unsupported.
|
Flags <code>#</code> and <code>0</code> are unsupported.
|
||||||
</p>
|
</p>
|
||||||
<div class="new">
|
|
||||||
<a name="timestamp"></a>
|
<a name="timestamp"></a>
|
||||||
<h2>14. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
<h2>16. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This format reads or writes timestamps and converts them to a double number.
|
This format reads or writes timestamps and converts them to a double number.
|
||||||
The value represents the number of seconds since 1970 (the UNIX epoch).
|
The value represents the number of seconds since 1970 (the UNIX epoch).
|
||||||
@ -623,7 +707,7 @@ In output, the system function <em>strftime()</em> is used to format the time.
|
|||||||
There may be differences in the implementation between operating systems.
|
There may be differences in the implementation between operating systems.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In input, <em>StreamDevice</em> used its own implementation because many
|
In input, <em>StreamDevice</em> uses its own implementation because many
|
||||||
systems are missing the <em>strptime()</em> function and additional formats
|
systems are missing the <em>strptime()</em> function and additional formats
|
||||||
are supported.
|
are supported.
|
||||||
</p>
|
</p>
|
||||||
@ -637,9 +721,8 @@ Because of the complexity of the problem, locales are not supported.
|
|||||||
Thus, only the English month names can be used (week day names are
|
Thus, only the English month names can be used (week day names are
|
||||||
ignored anyway).
|
ignored anyway).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
<hr>
|
<hr>
|
||||||
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
||||||
<p><small>Dirk Zimoch, 2011</small></p>
|
<p><small>Dirk Zimoch, 2015</small></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -104,13 +104,8 @@ This marks text you typically type in configuration files etc.
|
|||||||
<pre>
|
<pre>
|
||||||
Longer code segments are often set in a box.
|
Longer code segments are often set in a box.
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
|
||||||
Important modifications and new features are
|
|
||||||
<span class="new">highlighted with a yellow background</span>.
|
|
||||||
Obsolete features are <strike class="new">highlighted and crossed out</strike>.
|
|
||||||
</p>
|
|
||||||
<hr>
|
<hr>
|
||||||
<p align="right"><a href="setup.html">Next: Setup</a></p>
|
<p align="right"><a href="setup.html">Next: Setup</a></p>
|
||||||
<p><small>Dirk Zimoch, 2011</small></p>
|
<p><small>Dirk Zimoch, 2015</small></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -132,6 +132,7 @@ div div div a {list-style-type:circle;}
|
|||||||
<a target="_parent" href="formats.html#bcd" title="Binary coded decimal LONG converter">%D</a>
|
<a target="_parent" href="formats.html#bcd" title="Binary coded decimal LONG converter">%D</a>
|
||||||
<a target="_parent" href="formats.html#chksum" title="Checksum pseudo converter">%<<em>checksum</em>></a>
|
<a target="_parent" href="formats.html#chksum" title="Checksum pseudo converter">%<<em>checksum</em>></a>
|
||||||
<a target="_parent" href="formats.html#regex" title="Perl regular expression STRING converter">%/<em>regex</em>/</a>
|
<a target="_parent" href="formats.html#regex" title="Perl regular expression STRING converter">%/<em>regex</em>/</a>
|
||||||
|
<a target="_parent" href="formats.html#regsub" title="Perl regular expression substitution pseudo converter">%#/<em>regex</em>/<em>subst</em>/</a>
|
||||||
<a target="_parent" href="formats.html#mantexp" title="MantissaExponent DOUBLE converter">%m</a>
|
<a target="_parent" href="formats.html#mantexp" title="MantissaExponent DOUBLE converter">%m</a>
|
||||||
<a target="_parent" href="formats.html#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
<a target="_parent" href="formats.html#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,6 +88,16 @@ code {
|
|||||||
text-align:left;
|
text-align:left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin-left:1ex;
|
||||||
|
margin-right:1ex;
|
||||||
|
margin-top:0.5ex;
|
||||||
|
padding: 0 1ex;
|
||||||
|
border: 1px solid black;
|
||||||
|
text-align:left;
|
||||||
|
background-color:#f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
#navleft {
|
#navleft {
|
||||||
position:fixed;
|
position:fixed;
|
||||||
left:0;
|
left:0;
|
||||||
|
@ -562,13 +562,7 @@ connectToAsynPort()
|
|||||||
clientName(), connected ? "already" : "not yet");
|
clientName(), connected ? "already" : "not yet");
|
||||||
if (!connected)
|
if (!connected)
|
||||||
{
|
{
|
||||||
printf ("%s: AsynDriverInterface::connectToAsynPort: "
|
|
||||||
"pasynCommon->connect(%p, %p)\n",
|
|
||||||
clientName(), pvtCommon, pasynUser);
|
|
||||||
status = pasynCommon->connect(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): "
|
debug("AsynDriverInterface::connectToAsynPort(%s): "
|
||||||
"status=%s\n",
|
"status=%s\n",
|
||||||
clientName(), asynStatusStr[status]);
|
clientName(), asynStatusStr[status]);
|
||||||
@ -718,13 +712,19 @@ writeHandler()
|
|||||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
pasynUser->errorMessage[0] = 0;
|
||||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||||
outputBuffer, outputSize, &written);
|
outputBuffer, outputSize, &written);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
debug("AsynDriverInterface::writeHandler(%s): "
|
debug("AsynDriverInterface::writeHandler(%s): "
|
||||||
"write(..., outputSize=%ld, written=%ld) "
|
"write(..., \"%s\", outputSize=%ld, written=%ld) "
|
||||||
"[timeout=%g sec] = %s\n",
|
"[timeout=%g sec] = %s (%s)\n",
|
||||||
clientName(), (long)outputSize, (long)written,
|
clientName(),
|
||||||
pasynUser->timeout, asynStatusStr[status]);
|
StreamBuffer(outputBuffer, outputSize).expand()(),
|
||||||
|
(long)outputSize, (long)written,
|
||||||
|
pasynUser->timeout, asynStatusStr[status],
|
||||||
|
pasynUser->errorMessage);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (oldeoslen >= 0) // restore asyn terminator
|
if (oldeoslen >= 0) // restore asyn terminator
|
||||||
{
|
{
|
||||||
@ -769,34 +769,34 @@ writeHandler()
|
|||||||
writeCallback(StreamIoSuccess);
|
writeCallback(StreamIoSuccess);
|
||||||
return;
|
return;
|
||||||
case asynTimeout:
|
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);
|
clientName(), pasynUser->timeout, pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoTimeout);
|
writeCallback(StreamIoTimeout);
|
||||||
return;
|
return;
|
||||||
case asynOverflow:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynError:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||||
case asynDisconnected:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynDisabled:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
@ -887,6 +887,11 @@ readHandler()
|
|||||||
oldeoslen = -1;
|
oldeoslen = -1;
|
||||||
} else do {
|
} else do {
|
||||||
// device (e.g. GPIB) might not accept full eos length
|
// 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,
|
if (pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||||
deveos, deveoslen) == asynSuccess)
|
deveos, deveoslen) == asynSuccess)
|
||||||
{
|
{
|
||||||
@ -894,8 +899,9 @@ readHandler()
|
|||||||
if (ioAction != AsyncRead)
|
if (ioAction != AsyncRead)
|
||||||
{
|
{
|
||||||
debug("AsynDriverInterface::readHandler(%s) "
|
debug("AsynDriverInterface::readHandler(%s) "
|
||||||
"input EOS set to %s\n",
|
"input EOS changed from \"%s\" to \"%s\"\n",
|
||||||
clientName(),
|
clientName(),
|
||||||
|
StreamBuffer(oldeos, oldeoslen).expand()(),
|
||||||
StreamBuffer(deveos, deveoslen).expand()());
|
StreamBuffer(deveos, deveoslen).expand()());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -951,6 +957,7 @@ readHandler()
|
|||||||
readMore = 0;
|
readMore = 0;
|
||||||
received = 0;
|
received = 0;
|
||||||
eomReason = 0;
|
eomReason = 0;
|
||||||
|
pasynUser->errorMessage[0] = 0;
|
||||||
|
|
||||||
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
||||||
"read(..., bytesToRead=%ld, ...) "
|
"read(..., bytesToRead=%ld, ...) "
|
||||||
@ -959,12 +966,13 @@ readHandler()
|
|||||||
bytesToRead, pasynUser->timeout);
|
bytesToRead, pasynUser->timeout);
|
||||||
status = pasynOctet->read(pvtOctet, pasynUser,
|
status = pasynOctet->read(pvtOctet, pasynUser,
|
||||||
buffer, bytesToRead, &received, &eomReason);
|
buffer, bytesToRead, &received, &eomReason);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
debug("AsynDriverInterface::readHandler(%s): "
|
debug("AsynDriverInterface::readHandler(%s): "
|
||||||
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
||||||
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
||||||
(long)received,eomReasonStr[eomReason&0x7],
|
(long)received,eomReasonStr[eomReason&0x7],
|
||||||
StreamBuffer(buffer, received).expand()());
|
StreamBuffer(buffer, received).expand()());
|
||||||
|
#endif
|
||||||
pasynManager->isConnected(pasynUser, &connected);
|
pasynManager->isConnected(pasynUser, &connected);
|
||||||
debug("AsynDriverInterface::readHandler(%s): "
|
debug("AsynDriverInterface::readHandler(%s): "
|
||||||
"device is now %sconnected\n",
|
"device is now %sconnected\n",
|
||||||
@ -1094,29 +1102,29 @@ readHandler()
|
|||||||
}
|
}
|
||||||
peeksize = inputBuffer.capacity();
|
peeksize = inputBuffer.capacity();
|
||||||
// deliver whatever we could save
|
// 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);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault, buffer, received);
|
readCallback(StreamIoFault, buffer, received);
|
||||||
break;
|
break;
|
||||||
case asynError:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault, buffer, received);
|
readCallback(StreamIoFault, buffer, received);
|
||||||
break;
|
break;
|
||||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||||
case asynDisconnected:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynDisabled:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
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);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
@ -1138,10 +1146,16 @@ readHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore original EOS
|
// restore original EOS
|
||||||
if (oldeoslen >= 0)
|
if (oldeoslen >= 0 && oldeoslen != (int)deveoslen && strcmp(deveos, oldeos) != 0)
|
||||||
{
|
{
|
||||||
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||||
oldeos, oldeoslen);
|
oldeos, oldeoslen);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
|
debug("AsynDriverInterface::readHandler(%s) "
|
||||||
|
"input EOS restored to \"%s\"\n",
|
||||||
|
clientName(),
|
||||||
|
StreamBuffer(oldeos, oldeoslen).expand()());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,21 @@
|
|||||||
#include "StreamError.h"
|
#include "StreamError.h"
|
||||||
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
|
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
|
||||||
// These systems have no strncasecmp
|
// These systems have no strncasecmp
|
||||||
|
#include <epicsVersion.h>
|
||||||
|
#ifdef BASE_VERSION
|
||||||
|
// 3.13
|
||||||
|
#include <ctype.h>
|
||||||
|
static int strncasecmp(const char *s1, const char *s2, size_t n)
|
||||||
|
{
|
||||||
|
int r=0;
|
||||||
|
while (n && (r = toupper(*s1)-toupper(*s2)) == 0) { n--; s1++; s2++; };
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#else
|
||||||
#include <epicsString.h>
|
#include <epicsString.h>
|
||||||
#define strncasecmp epicsStrnCaseCmp
|
#define strncasecmp epicsStrnCaseCmp
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
typedef unsigned int (*checksumFunc)(const unsigned char* data, unsigned int len, unsigned int init);
|
typedef unsigned int (*checksumFunc)(const unsigned char* data, unsigned int len, unsigned int init);
|
||||||
@ -469,6 +481,7 @@ static checksum checksumMap[] =
|
|||||||
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
|
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
|
||||||
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
|
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
|
||||||
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
|
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
|
||||||
|
{"modbus", crc_0x8005_r, 0xFFFF, 0x0000, 2}, // 0x4B37
|
||||||
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
|
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
|
||||||
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
|
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
|
||||||
{"ccitt16x",crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
{"ccitt16x",crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "pcre.h"
|
#include "pcre.h"
|
||||||
|
|
||||||
// Perl regular expressions (PCRE) %/regexp/
|
// Perl regular expressions (PCRE) %/regexp/ and %#/regexp/subst/
|
||||||
|
|
||||||
/* Notes:
|
/* Notes:
|
||||||
- Memory for compiled regexp is allocated in parse but never freed.
|
- Memory for compiled regexp is allocated in parse but never freed.
|
||||||
@ -32,61 +32,93 @@
|
|||||||
run-time leak.
|
run-time leak.
|
||||||
- A maximum of 9 subexpressions is supported. Only one of them can
|
- A maximum of 9 subexpressions is supported. Only one of them can
|
||||||
be the result of the match.
|
be the result of the match.
|
||||||
- vxWorks and maybe other OS don't have a PCRE library. Provide one?
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class RegexpConverter : public StreamFormatConverter
|
class RegexpConverter : public StreamFormatConverter
|
||||||
{
|
{
|
||||||
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
|
int parse (const StreamFormat& fmt, StreamBuffer&, const char*&, bool);
|
||||||
int scanString(const StreamFormat&, const char*, char*, size_t);
|
int scanString(const StreamFormat& fmt, const char*, char*, size_t);
|
||||||
|
int scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor);
|
||||||
|
bool printPseudo(const StreamFormat& fmt, StreamBuffer& output);
|
||||||
};
|
};
|
||||||
|
|
||||||
int RegexpConverter::
|
int RegexpConverter::
|
||||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||||
const char*& source, bool scanFormat)
|
const char*& source, bool scanFormat)
|
||||||
{
|
{
|
||||||
if (!scanFormat)
|
if (!scanFormat && !(fmt.flags & alt_flag))
|
||||||
{
|
{
|
||||||
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fmt.prec > 9)
|
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;
|
|
||||||
}
|
|
||||||
if (fmt.flags & (left_flag|space_flag|zero_flag|alt_flag))
|
|
||||||
{
|
|
||||||
error("Use of modifiers '-', ' ', '0', '#'"
|
|
||||||
"not allowed with %%/regexp/ conversion\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuffer pattern;
|
StreamBuffer pattern;
|
||||||
while (*source != '/')
|
while (*source != '/')
|
||||||
{
|
{
|
||||||
if (!*source) {
|
if (!*source) {
|
||||||
error("Missing closing '/' after %%/ format conversion\n");
|
error("Missing closing '/' after %%/%s format conversion\n", pattern());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (*source == esc) {
|
if (*source == esc) { // handle escaped chars
|
||||||
source++;
|
if (*++source != '/') // just un-escape /
|
||||||
pattern.print("\\x%02x", *source++ & 0xFF);
|
{
|
||||||
|
pattern.append('\\');
|
||||||
|
if ((*source & 0x7f) < 0x30) // handle control chars
|
||||||
|
{
|
||||||
|
pattern.print("x%02x", *source++);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// fall through for PCRE codes like \B
|
||||||
|
}
|
||||||
|
}
|
||||||
pattern.append(*source++);
|
pattern.append(*source++);
|
||||||
}
|
}
|
||||||
source++;
|
source++;
|
||||||
debug("regexp = \"%s\"\n", pattern());
|
debug("regexp = \"%s\"\n", pattern.expand()());
|
||||||
|
|
||||||
const char* errormsg;
|
const char* errormsg;
|
||||||
int eoffset;
|
int eoffset;
|
||||||
pcre* code = pcre_compile(pattern(), 0,
|
int nsubexpr;
|
||||||
&errormsg, &eoffset, NULL);
|
|
||||||
|
pcre* code = pcre_compile(pattern(), 0, &errormsg, &eoffset, NULL);
|
||||||
if (!code)
|
if (!code)
|
||||||
{
|
{
|
||||||
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
|
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
|
||||||
return false;
|
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));
|
info.append(&code, sizeof(code));
|
||||||
|
|
||||||
|
if (fmt.flags & alt_flag)
|
||||||
|
{
|
||||||
|
StreamBuffer subst;
|
||||||
|
|
||||||
|
debug("check for subst in \"%s\"\n", StreamBuffer(source).expand()());
|
||||||
|
while (*source != '/')
|
||||||
|
{
|
||||||
|
if (!*source) {
|
||||||
|
error("Missing closing '/' after %%#/%s/%s format conversion\n", pattern(), subst());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (*source == esc)
|
||||||
|
subst.append(*source++);
|
||||||
|
subst.append(*source++);
|
||||||
|
}
|
||||||
|
source++;
|
||||||
|
debug("subst = \"%s\"\n", subst.expand()());
|
||||||
|
info.append(subst).append('\0');
|
||||||
|
return pseudo_format;
|
||||||
|
}
|
||||||
return string_format;
|
return string_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,32 +126,130 @@ int RegexpConverter::
|
|||||||
scanString(const StreamFormat& fmt, const char* input,
|
scanString(const StreamFormat& fmt, const char* input,
|
||||||
char* value, size_t maxlen)
|
char* value, size_t maxlen)
|
||||||
{
|
{
|
||||||
pcre* code;
|
|
||||||
size_t len;
|
|
||||||
int ovector[30];
|
int ovector[30];
|
||||||
int rc;
|
int rc;
|
||||||
int subexpr = 0;
|
unsigned int l;
|
||||||
|
|
||||||
memcpy (&code, fmt.info, sizeof(code));
|
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;
|
||||||
|
|
||||||
len = fmt.width > 0 ? fmt.width : strlen(input);
|
debug("input = \"%s\"\n", input);
|
||||||
subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
debug("length=%d\n", length);
|
||||||
rc = pcre_exec(code, NULL, input, len, 0, 0, ovector, 30);
|
|
||||||
if (rc < 1) return -1;
|
rc = pcre_exec(code, NULL, input, length, 0, 0, ovector, 30);
|
||||||
if (fmt.flags & skip_flag) return ovector[1];
|
debug("pcre_exec match \"%.*s\" result = %d\n", length, input, rc);
|
||||||
len = ovector[subexpr*2+1] - ovector[subexpr*2];
|
if ((subexpr && rc <= subexpr) || rc < 0)
|
||||||
if (len >= maxlen) {
|
{
|
||||||
if (!(fmt.flags & sign_flag)) {
|
// error or no match or not enough sub-expressions
|
||||||
error("Regexp: Matching string \"%s\" too long (%d>%d bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
|
||||||
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
|
|
||||||
(int)len, (int)maxlen-1);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
len = maxlen-1;
|
if (fmt.flags & skip_flag) return ovector[subexpr*2+1];
|
||||||
|
|
||||||
|
l = ovector[subexpr*2+1] - ovector[subexpr*2];
|
||||||
|
if (l >= maxlen) {
|
||||||
|
if (!(fmt.flags & sign_flag)) {
|
||||||
|
error("Regexp: Matching string \"%s\" too long (%d>%ld bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||||
|
StreamBuffer(input + ovector[subexpr*2],l).expand()(),
|
||||||
|
l, (long)maxlen-1);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(value, input+ovector[subexpr*2], len);
|
l = maxlen-1;
|
||||||
value[len]=0;
|
}
|
||||||
return ovector[1];
|
memcpy(value, input + ovector[subexpr*2], l);
|
||||||
|
value[l] = '\0';
|
||||||
|
return ovector[1]; // consume input until end of match
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
|
||||||
|
{
|
||||||
|
const char* subst = fmt.info;
|
||||||
|
pcre* code = extract<pcre*>(subst);
|
||||||
|
long length;
|
||||||
|
int rc, l, c, r, rl, n;
|
||||||
|
int ovector[30];
|
||||||
|
StreamBuffer s;
|
||||||
|
|
||||||
|
length = buffer.length() - start;
|
||||||
|
if (fmt.width && fmt.width < length)
|
||||||
|
length = fmt.width;
|
||||||
|
if (fmt.flags & left_flag)
|
||||||
|
start = buffer.length() - length;
|
||||||
|
|
||||||
|
debug("regsubst buffer=\"%s\", start=%ld, length=%ld, subst = \"%s\"\n",
|
||||||
|
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", 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
|
||||||
|
c += ovector[1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// replace & by match in subst
|
||||||
|
l = ovector[1] - 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, 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()());
|
||||||
|
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 (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, buffer.expand(start+c+ovector[ch], rl)());
|
||||||
|
s.replace(r, 2, buffer(start+c+ovector[ch]), rl);
|
||||||
|
r += rl - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s.remove(r, 1); // just remove escape
|
||||||
|
}
|
||||||
|
else if (s[r] == '&') // unescaped & : replace with match
|
||||||
|
{
|
||||||
|
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.expand()());
|
||||||
|
}
|
||||||
|
buffer.replace(start+c+ovector[0], l, s);
|
||||||
|
length += s.length() - l;
|
||||||
|
c += ovector[0] + s.length();
|
||||||
|
if (n == fmt.prec) // max match reached
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RegexpConverter::
|
||||||
|
scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor)
|
||||||
|
{
|
||||||
|
/* re-write input buffer */
|
||||||
|
regsubst(fmt, input, cursor);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegexpConverter::
|
||||||
|
printPseudo(const StreamFormat& fmt, StreamBuffer& output)
|
||||||
|
{
|
||||||
|
/* re-write output buffer */
|
||||||
|
regsubst(fmt, output, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterConverter (RegexpConverter, "/");
|
RegisterConverter (RegexpConverter, "/");
|
||||||
|
@ -296,7 +296,7 @@ StreamBuffer StreamBuffer::expand(ssize_t start, ssize_t length) const
|
|||||||
for (i = start; i < end; i++)
|
for (i = start; i < end; i++)
|
||||||
{
|
{
|
||||||
c = buffer[i];
|
c = buffer[i];
|
||||||
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
|
if (c < 0x20 || c >= 0x7f)
|
||||||
{
|
{
|
||||||
result.print("<%02x>", c & 0xff);
|
result.print("<%02x>", c & 0xff);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ finishProtocol(ProtocolResult status)
|
|||||||
status = Fault;
|
status = Fault;
|
||||||
}
|
}
|
||||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||||
if (runningHandler)
|
if (runningHandler || flags & InitRun)
|
||||||
{
|
{
|
||||||
// get original error status
|
// get original error status
|
||||||
if (status == Success) status = runningHandler;
|
if (status == Success) status = runningHandler;
|
||||||
@ -832,7 +832,7 @@ lockCallback(StreamIoStatus status)
|
|||||||
case StreamIoSuccess:
|
case StreamIoSuccess:
|
||||||
break;
|
break;
|
||||||
case StreamIoTimeout:
|
case StreamIoTimeout:
|
||||||
error("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
debug("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
||||||
name(), lockTimeout);
|
name(), lockTimeout);
|
||||||
flags &= ~BusOwner;
|
flags &= ~BusOwner;
|
||||||
finishProtocol(LockTimeout);
|
finishProtocol(LockTimeout);
|
||||||
@ -991,7 +991,7 @@ readCallback(StreamIoStatus status,
|
|||||||
evalIn();
|
evalIn();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error("%s: No reply from device within %ld ms\n",
|
debug("StreamCore::readCallback(%s): No reply from device within %ld ms\n",
|
||||||
name(), replyTimeout);
|
name(), replyTimeout);
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
finishProtocol(ReplyTimeout);
|
finishProtocol(ReplyTimeout);
|
||||||
@ -1479,6 +1479,7 @@ scanValue(const StreamFormat& fmt, long& value)
|
|||||||
}
|
}
|
||||||
else return -1;
|
else return -1;
|
||||||
}
|
}
|
||||||
|
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||||
debug("StreamCore::scanValue(%s) scanned %li\n",
|
debug("StreamCore::scanValue(%s) scanned %li\n",
|
||||||
name(), value);
|
name(), value);
|
||||||
@ -1510,6 +1511,7 @@ scanValue(const StreamFormat& fmt, double& value)
|
|||||||
}
|
}
|
||||||
else return -1;
|
else return -1;
|
||||||
}
|
}
|
||||||
|
if (fmt.flags & fix_width_flag && (consumed != (fmt.width + fmt.prec + 1))) return -1;
|
||||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||||
debug("StreamCore::scanValue(%s) scanned %#g\n",
|
debug("StreamCore::scanValue(%s) scanned %#g\n",
|
||||||
name(), value);
|
name(), value);
|
||||||
@ -1542,6 +1544,7 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
|
|||||||
}
|
}
|
||||||
else return -1;
|
else return -1;
|
||||||
}
|
}
|
||||||
|
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||||
#ifndef NO_TEMPORARY
|
#ifndef NO_TEMPORARY
|
||||||
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
|
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
|
||||||
|
@ -115,7 +115,6 @@ class Stream : protected StreamCore
|
|||||||
epicsMutex mutex;
|
epicsMutex mutex;
|
||||||
epicsEvent initDone;
|
epicsEvent initDone;
|
||||||
#endif
|
#endif
|
||||||
StreamBuffer fieldBuffer;
|
|
||||||
int status;
|
int status;
|
||||||
int convert;
|
int convert;
|
||||||
long currentValueLength;
|
long currentValueLength;
|
||||||
@ -181,6 +180,7 @@ public:
|
|||||||
#ifndef EPICS_3_13
|
#ifndef EPICS_3_13
|
||||||
extern "C" {
|
extern "C" {
|
||||||
epicsExportAddress(int, streamDebug);
|
epicsExportAddress(int, streamDebug);
|
||||||
|
epicsExportAddress(int, streamError);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -196,8 +196,12 @@ extern "C" long streamReload(char* recordname)
|
|||||||
dbCommon* record;
|
dbCommon* record;
|
||||||
long status;
|
long status;
|
||||||
|
|
||||||
|
int oldStreamError = streamError;
|
||||||
|
streamError = 1;
|
||||||
|
|
||||||
if(!pdbbase) {
|
if(!pdbbase) {
|
||||||
error("No database has been loaded\n");
|
error("No database has been loaded\n");
|
||||||
|
streamError = oldStreamError;
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
debug("streamReload(%s)\n", recordname);
|
debug("streamReload(%s)\n", recordname);
|
||||||
@ -234,6 +238,7 @@ extern "C" long streamReload(char* recordname)
|
|||||||
}
|
}
|
||||||
dbFinishEntry(&dbentry);
|
dbFinishEntry(&dbentry);
|
||||||
StreamProtocolParser::free();
|
StreamProtocolParser::free();
|
||||||
|
streamError = oldStreamError;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +413,7 @@ long streamInit(int after)
|
|||||||
{
|
{
|
||||||
if (after)
|
if (after)
|
||||||
{
|
{
|
||||||
|
streamError = 0; // Switch off errors after init in order not to spam messages when a device is down.
|
||||||
StreamProtocolParser::free();
|
StreamProtocolParser::free();
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
@ -604,7 +610,7 @@ parseLink(const struct link *ioLink, char* filename,
|
|||||||
if (items <= 0)
|
if (items <= 0)
|
||||||
{
|
{
|
||||||
error("%s: Empty I/O link. "
|
error("%s: Empty I/O link. "
|
||||||
"Forgot the leading '@' or confused INP with OUT ?\n",
|
"Forgot the leading '@' or confused INP with OUT or link is too long ?\n",
|
||||||
name());
|
name());
|
||||||
return S_dev_badInitRet;
|
return S_dev_badInitRet;
|
||||||
}
|
}
|
||||||
@ -735,16 +741,15 @@ process()
|
|||||||
debug("Stream::process(%s) start\n", name());
|
debug("Stream::process(%s) start\n", name());
|
||||||
status = NO_ALARM;
|
status = NO_ALARM;
|
||||||
convert = OK;
|
convert = OK;
|
||||||
record->pact = true;
|
|
||||||
if (!startProtocol(StreamCore::StartNormal))
|
if (!startProtocol(StreamCore::StartNormal))
|
||||||
{
|
{
|
||||||
debug("Stream::process(%s): could not start, status=%d\n",
|
debug("Stream::process(%s): could not start, status=%d\n",
|
||||||
name(), status);
|
name(), status);
|
||||||
(void) recGblSetSevr(record, status, INVALID_ALARM);
|
(void) recGblSetSevr(record, status, INVALID_ALARM);
|
||||||
record->pact = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
debug("Stream::process(%s): protocol started\n", name());
|
debug("Stream::process(%s): protocol started\n", name());
|
||||||
|
record->pact = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,6 +995,7 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
|||||||
{
|
{
|
||||||
// Format like "%([record.]field)..." has requested to get value
|
// Format like "%([record.]field)..." has requested to get value
|
||||||
// from field of this or other record.
|
// from field of this or other record.
|
||||||
|
StreamBuffer fieldBuffer;
|
||||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||||
|
|
||||||
/* Handle time stamps special. %T converter takes double. */
|
/* Handle time stamps special. %T converter takes double. */
|
||||||
@ -1134,6 +1140,7 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
|||||||
{
|
{
|
||||||
// Format like "%([record.]field)..." has requested to put value
|
// Format like "%([record.]field)..." has requested to put value
|
||||||
// to field of this or other record.
|
// to field of this or other record.
|
||||||
|
StreamBuffer fieldBuffer;
|
||||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||||
long nord;
|
long nord;
|
||||||
long nelem = pdbaddr->no_elements;
|
long nelem = pdbaddr->no_elements;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int streamDebug = 0;
|
int streamDebug = 0;
|
||||||
|
int streamError = 1;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
__declspec(dllexport)
|
__declspec(dllexport)
|
||||||
@ -74,6 +75,7 @@ void StreamError(int line, const char* file, const char* fmt, ...)
|
|||||||
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
||||||
{
|
{
|
||||||
char timestamp[40];
|
char timestamp[40];
|
||||||
|
if (!streamError) return; // Error logging disabled
|
||||||
StreamPrintTimestampFunction(timestamp, 40);
|
StreamPrintTimestampFunction(timestamp, 40);
|
||||||
#ifdef va_copy
|
#ifdef va_copy
|
||||||
if (StreamDebugFile)
|
if (StreamDebugFile)
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int streamDebug;
|
extern int streamDebug;
|
||||||
|
extern int streamError;
|
||||||
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
||||||
|
|
||||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||||
|
@ -30,7 +30,8 @@ typedef enum {
|
|||||||
zero_flag = 0x10,
|
zero_flag = 0x10,
|
||||||
skip_flag = 0x20,
|
skip_flag = 0x20,
|
||||||
default_flag = 0x40,
|
default_flag = 0x40,
|
||||||
compare_flag = 0x80
|
compare_flag = 0x80,
|
||||||
|
fix_width_flag = 0x100,
|
||||||
} StreamFormatFlag;
|
} StreamFormatFlag;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -48,7 +49,7 @@ typedef struct StreamFormat
|
|||||||
{
|
{
|
||||||
char conv;
|
char conv;
|
||||||
StreamFormatType type;
|
StreamFormatType type;
|
||||||
unsigned char flags;
|
unsigned short flags;
|
||||||
short prec;
|
short prec;
|
||||||
unsigned short width;
|
unsigned short width;
|
||||||
unsigned short infolen;
|
unsigned short infolen;
|
||||||
|
@ -38,7 +38,7 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
source := [flags] [width] ['.' prec] conv [extra]
|
source := [flags] [width] ['.' prec] conv [extra]
|
||||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '='
|
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '=' | '!'
|
||||||
width := integer
|
width := integer
|
||||||
prec := integer
|
prec := integer
|
||||||
conv := character
|
conv := character
|
||||||
@ -85,6 +85,15 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
|||||||
}
|
}
|
||||||
streamFormat.flags |= default_flag;
|
streamFormat.flags |= default_flag;
|
||||||
break;
|
break;
|
||||||
|
case '!':
|
||||||
|
if (formatType != ScanFormat)
|
||||||
|
{
|
||||||
|
error("Use of fixed width modifier '!' "
|
||||||
|
"only allowed in input formats\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
streamFormat.flags |= fix_width_flag;
|
||||||
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
if (formatType != ScanFormat)
|
if (formatType != ScanFormat)
|
||||||
{
|
{
|
||||||
|
@ -512,7 +512,16 @@ Each time newline is read, line is incremented.
|
|||||||
buffer(token));
|
buffer(token));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (c == '$' && buffer[-1] == '\\')
|
buffer.append(c);
|
||||||
|
if (c == quote)
|
||||||
|
{
|
||||||
|
quote = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
c = getc(file);
|
||||||
|
if (c == '$')
|
||||||
{
|
{
|
||||||
// quoted variable reference
|
// quoted variable reference
|
||||||
// terminate string here and do variable in next pass
|
// terminate string here and do variable in next pass
|
||||||
@ -520,11 +529,13 @@ Each time newline is read, line is incremented.
|
|||||||
ungetc(c, file);
|
ungetc(c, file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buffer.append(c);
|
if (c == EOF || c == '\n')
|
||||||
if (c == quote && buffer[-2] != '\\')
|
|
||||||
{
|
{
|
||||||
quote = false;
|
error(line, filename(), "Backslash at end of line: %s\n",
|
||||||
break;
|
buffer(token));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer.append(c);
|
||||||
}
|
}
|
||||||
c = getc(file);
|
c = getc(file);
|
||||||
}
|
}
|
||||||
@ -1348,7 +1359,8 @@ compileString(StreamBuffer& buffer, const char*& source,
|
|||||||
source);
|
source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (formatType != NoFormat)
|
if (formatType != NoFormat &&
|
||||||
|
i > 2 /* do not escape skip */)
|
||||||
{
|
{
|
||||||
buffer.append(esc);
|
buffer.append(esc);
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
#define devStream_h
|
#define devStream_h
|
||||||
|
|
||||||
#define STREAM_MAJOR 2
|
#define STREAM_MAJOR 2
|
||||||
#define STREAM_MINOR 6
|
#define STREAM_MINOR 7
|
||||||
#define STREAM_PATCHLEVEL 6
|
#define STREAM_PATCHLEVEL 14
|
||||||
|
|
||||||
#if defined(__vxworks) || defined(vxWorks)
|
#if defined(__vxworks) || defined(vxWorks)
|
||||||
#include <vxWorks.h>
|
#include <vxWorks.h>
|
||||||
@ -58,7 +58,6 @@ extern "C" {
|
|||||||
#include <dbCommon.h>
|
#include <dbCommon.h>
|
||||||
#include <dbScan.h>
|
#include <dbScan.h>
|
||||||
#include <devSup.h>
|
#include <devSup.h>
|
||||||
/* #include <dbFldTypes.h> */
|
|
||||||
#include <dbAccess.h>
|
#include <dbAccess.h>
|
||||||
|
|
||||||
#ifdef devStream_epicsExportSharedSymbols
|
#ifdef devStream_epicsExportSharedSymbols
|
||||||
|
@ -55,11 +55,14 @@ static long readData (dbCommon *record, format_t *format)
|
|||||||
ai->rval = rval;
|
ai->rval = rval;
|
||||||
if (ai->linr == menuConvertNO_CONVERSION)
|
if (ai->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
|
double val;
|
||||||
if (format->type == DBF_ULONG)
|
if (format->type == DBF_ULONG)
|
||||||
ai->val = (unsigned long)rval;
|
val = (unsigned long)rval;
|
||||||
else
|
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 DO_NOT_CONVERT;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -46,11 +46,14 @@ static long readData (dbCommon *record, format_t *format)
|
|||||||
ao->rval = rval;
|
ao->rval = rval;
|
||||||
if (ao->linr == menuConvertNO_CONVERSION)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
|
double val;
|
||||||
if (format->type == DBF_ULONG)
|
if (format->type == DBF_ULONG)
|
||||||
ao->val = (unsigned long)rval;
|
val = (unsigned long)rval;
|
||||||
else
|
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 DO_NOT_CONVERT;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
@ -63,20 +66,21 @@ static long writeData (dbCommon *record, format_t *format)
|
|||||||
{
|
{
|
||||||
aoRecord *ao = (aoRecord *) record;
|
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)
|
switch (format->type)
|
||||||
{
|
{
|
||||||
case DBF_DOUBLE:
|
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);
|
return streamPrintf (record, format, val);
|
||||||
}
|
}
|
||||||
case DBF_ULONG:
|
case DBF_ULONG:
|
||||||
{
|
{
|
||||||
if (ao->linr == menuConvertNO_CONVERSION)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
return streamPrintf (record, format, (unsigned long)(INIT_RUN ? ao->val : ao->oval));
|
return streamPrintf (record, format, (unsigned long)val);
|
||||||
}
|
}
|
||||||
return streamPrintf (record, format, (unsigned long)ao->rval);
|
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)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
return streamPrintf (record, format, (long)(INIT_RUN ? ao->val : ao->oval));
|
return streamPrintf (record, format, (long)val);
|
||||||
}
|
}
|
||||||
return streamPrintf (record, format, (long)ao->rval);
|
return streamPrintf (record, format, (long)ao->rval);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
#include <mbboDirectRecord.h>
|
#include <mbboDirectRecord.h>
|
||||||
|
#include "alarm.h"
|
||||||
#include "devStream.h"
|
#include "devStream.h"
|
||||||
#include <epicsExport.h>
|
#include <epicsExport.h>
|
||||||
|
|
||||||
@ -67,9 +68,27 @@ static long initRecord (dbCommon *record)
|
|||||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||||
|
|
||||||
mbboD->mask <<= mbboD->shft;
|
mbboD->mask <<= mbboD->shft;
|
||||||
|
|
||||||
|
/* Workaround for bug in mbboDirect record:
|
||||||
|
Put to VAL overwrites value to 0 if SEVR is INVALID_ALARM
|
||||||
|
Thus first write may send a wrong value.
|
||||||
|
*/
|
||||||
|
mbboD->sevr = 0;
|
||||||
return streamInitRecord (record, &mbboD->out, readData, writeData);
|
return streamInitRecord (record, &mbboD->out, readData, writeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unfortunately the bug also corrupts the next write to VAL after an I/O error.
|
||||||
|
Thus make sure the record is never left in INVALID_ALARM status.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static long write(dbCommon *record)
|
||||||
|
{
|
||||||
|
long status = streamWrite(record);
|
||||||
|
if (record->nsev == INVALID_ALARM) record->nsev = MAJOR_ALARM;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
long number;
|
long number;
|
||||||
DEVSUPFUN report;
|
DEVSUPFUN report;
|
||||||
@ -83,7 +102,7 @@ struct {
|
|||||||
streamInit,
|
streamInit,
|
||||||
initRecord,
|
initRecord,
|
||||||
streamGetIointInfo,
|
streamGetIointInfo,
|
||||||
streamWrite
|
write
|
||||||
};
|
};
|
||||||
|
|
||||||
epicsExportAddress(dset,devmbboDirectStream);
|
epicsExportAddress(dset,devmbboDirectStream);
|
||||||
|
@ -192,6 +192,7 @@ static long writeData (dbCommon *record, format_t *format)
|
|||||||
return ERROR;
|
return ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DBF_ULONG:
|
||||||
case DBF_LONG:
|
case DBF_LONG:
|
||||||
case DBF_ENUM:
|
case DBF_ENUM:
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@ if (@ARGV[0] == "-3.13") {
|
|||||||
shift;
|
shift;
|
||||||
} else {
|
} else {
|
||||||
print "variable(streamDebug, int)\n";
|
print "variable(streamDebug, int)\n";
|
||||||
|
print "variable(streamError, int)\n";
|
||||||
print "registrar(streamRegistrar)\n";
|
print "registrar(streamRegistrar)\n";
|
||||||
}
|
}
|
||||||
print "driver(stream)\n";
|
print "driver(stream)\n";
|
||||||
|
Reference in New Issue
Block a user