diff --git a/docs/protocol.html b/docs/protocol.html index 2d55710..01ada2e 100644 --- a/docs/protocol.html +++ b/docs/protocol.html @@ -482,12 +482,63 @@ To make this easier, protocol arguments can be used: move { out "\$1 GOTO %d"; }
-Now, the protocol can be references in the OUT
link
+Now the same protocol can be used in the OUT
link
of three different records as move(X)
,
move(Y)
and move(Z)
.
-Up to 9 parameters, referenced as $1
... $9
-can be specified in parentheses, separated by comma.
-The variable $0
is replaced by the name of the protocol.
+
+Up to 9 parameters can be specified in parentheses, separated by comma.
+In the protocol, they are referenced as $1
...
+$9
outside quotes or \$1
... \$9
+within quotes. The parameter $0
resolves to the protocol name.
+
+To make links more readable, one space is allowed before and after each comma +and the enclosing parentheses. This space is not part of the parameter string. +Any additional space is part of the parameter. +
+
+If a parameter contains matching pairs of parentheses, these and all commas
+inside are part of the parameter.
+This allows to pass parameter strings like (1,2)
easily without
+much escaping.
+
+Unmatched parentheses must be escaped with double backslash \\
+as well as must be commas outside pairs of parentheses.
+Double backslash is necessary because one backslash is already consumed by
+the db file parser.
+To pass a literal backslash in a parameter string use 4 backslashes
+\\\\
.
+
+Note that macros can be used in parameters. That makes it possible to +pass part of the record name to the protocol to be used in +redirections. +
+ ++record(ai, "$(PREFIX)recX5") { + field(DTYP, "stream") + field(INP, "@$(PROTOCOLFILE) read(5, X\\,Y $(PREFIX)) $(PORT)") +} +record(ai, "$(PREFIX)recY5") {} + +read { out 0x8$1 "READ \$2"; in "%f,%(\$3recY\$1)f" } ++
+The protocol resolves to: +
++read { out 0x85 "READ X,Y"; in "%f,%($(PREFIX)recY5)f" } ++
+Here $(PREFIX)
is replaced with its macro value.
+But be aware that the macro is actually replaced before the link is parsed so
+that macro values containing comma or parentheses may have unintended effects.
setPosition { out "POS %f"; diff --git a/src/StreamCore.cc b/src/StreamCore.cc index 4101e4c..3c1ee68 100644 --- a/src/StreamCore.cc +++ b/src/StreamCore.cc @@ -189,14 +189,26 @@ parse(const char* filename, const char* _protocolname) ssize_t i = protocolname.find('('); if (i >= 0) { - while (i >= 0) + while (i < (ssize_t)protocolname.length()) { if (protocolname[i-1] == ' ') protocolname.remove(--i, 1); // remove trailing space - protocolname[i] = '\0'; // replace '(' and ',' with '\0' + protocolname[i] = '\0'; // replace initial '(' and separating ',' with '\0' if (protocolname[i+1] == ' ') protocolname.remove(i+1, 1); // remove leading space - i = protocolname.find(',', i+1); + int brackets = 0; + do { + i++; + i += strcspn(protocolname(i), ",()\\"); + char c = protocolname[i]; + if (c == '(') brackets++; + else if (c == ')') brackets--; + else if (c == ',' && brackets <= 0) break; + else if (c == '\\') { + if (protocolname[i+1] == '\\') i++; // keep '\\' + else protocolname.remove(i, 1); // else skip over next char + } + } while (i < (ssize_t)protocolname.length()); } // should have closing parentheses if (protocolname[-1] != ')') @@ -206,9 +218,8 @@ parse(const char* filename, const char* _protocolname) } protocolname.truncate(-1); // remove ')' if (protocolname[-1] == ' ') - { protocolname.truncate(-1); // remove trailing space - } + debug("StreamCore::parse \"%s\" -> \"%s\"\n", _protocolname, protocolname.expand()()); } StreamProtocolParser::Protocol* protocol; protocol = StreamProtocolParser::getProtocol(filename, protocolname); diff --git a/src/StreamEpics.cc b/src/StreamEpics.cc index 55b4da4..c6799de 100644 --- a/src/StreamEpics.cc +++ b/src/StreamEpics.cc @@ -764,7 +764,7 @@ initRecord(char* linkstring /* modifiable copy */) } // attach to bus interface - debug("Stream::initRecord %s: attachBus(%s, %ld, \"%s\")\n", + debug("Stream::initRecord %s: attachBus(\"%s\", %ld, \"%s\")\n", name(), busname, addr, busparam); if (!attachBus(busname, addr, busparam)) { @@ -774,7 +774,7 @@ initRecord(char* linkstring /* modifiable copy */) } // parse protocol file - debug("Stream::initRecord %s: parse(%s, %s)\n", + debug("Stream::initRecord %s: parse(\"%s\", \"%s\")\n", name(), filename, protocol); if (!parse(filename, protocol)) { diff --git a/streamApp/tests/testParenthesesInParameters b/streamApp/tests/testParenthesesInParameters index a4c2b4b..de98c23 100755 --- a/streamApp/tests/testParenthesesInParameters +++ b/streamApp/tests/testParenthesesInParameters @@ -10,7 +10,28 @@ set records { record(ao, "DZ:test1") { field (DTYP, "stream") - field (OUT, "@test.proto test1(ARG(10)) device") + field (OUT, "@test.proto test1(ARG(10),20,30) device") + } + + record(ao, "DZ:test2") + { + field (DTYP, "stream") + field (OUT, "@test.proto test1 (ARG(10,20), 30) device") + } + record(ao, "DZ:test3") + { + field (DTYP, "stream") + field (OUT, "@test.proto test1 ( ARG ( 10 , 20 ) , 30 ) device") + } + record(ao, "DZ:test4") + { + field (DTYP, "stream") + field (OUT, "@test.proto test1( ARG \\( 10 , 20 , 30 ) device") + } + record(ao, "DZ:test5") + { + field (DTYP, "stream") + field (OUT, "@test.proto test1(\\ ARG\\,\\\\(10,20)\\,30) device") } } @@ -28,5 +49,13 @@ startioc put DZ:test1 "1" assure "VAL:ARG(10):1\n" +put DZ:test2 "1" +assure "VAL:ARG(10,20):1\n" +put DZ:test3 "1" +assure "VAL:ARG ( 10 , 20 ):1\n" +put DZ:test4 "1" +assure "VAL: ARG ( 10 :1\n" +put DZ:test5 "1" +assure "VAL: ARG,\\(10,20),30:1\n" -finish +#finish