Encoding a format involves replacing it with binary encoded information
that contains a NULL byte. This null byte terminates the search for
subsequent formats. This means that if the encoding is done when
the recursion depth is greater than 0, no format will be recognized as
such when the recursion depth decreases.
A real life manifestation of this bug:
wont_work {
prefix = "@" "%3c" "ACK";
in $prefix "%s";
}
The "%s" will be interpreted as literal '%' followed by 's'.
What happens here is that the variable $prefix will be replaced (by
recursively calling compileString()) with "@%3cACK" in the first step
and in the second step the format conversion will be replaced with a
binary encoding (sg like "<03>3c<00>..."). That NUL byte will cause the
search for '%' (in the parent compileString()) to stop, leaving the "%s" intact.
The fix is simple; defer the format encode until everything else is done.
To preserve binary compatibility a new private function is introduced
instead of changing the signature of the exising one.
This is done by remembering the last asynStatus in the AsynDriverInterface
object. In certain selected places, namely when pasynManager->queueRequest
or pasynCommon->connect is called, we report an error only if the new status
differs from the last one. This gives us just one message when the failure
occurs (or when the operation is tried for the first time) and another
message when the status returns back to asynSuccess.
Note this is currently limited to AsynDriverInterface. If a device is online
but responds in unexpected ways (including not at all) StreamDevice will
still report errors repeatedly whenever records are processed. Fixing this
is very well possible but beyond the scope of this patch.