Fix epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()

Major rewrites; the original epicsStrnRawFromEscaped() could read
beyond the end of the input buffer if the input count ended in the
middle of an octal or hex escape sequence. Zero termination did not
always match the return count, and hex escapes were always 2 digits,
contrary to the C standard.

New versions don't use epicsSnprintf() or sscanf() for hex and octal
conversions, so should be slightly faster.

Added 81 new tests to check the above issues.
This commit is contained in:
Andrew Johnson
2014-12-01 11:34:58 -06:00
parent 1a94ea1045
commit 30b789aa49
3 changed files with 316 additions and 136 deletions

View File

@@ -14,6 +14,11 @@
<h2 align="center">Changes between 3.15.0.2 and 3.15.1</h2>
<!-- Insert new items immediately below here ... -->
<h3>epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()</h3>
<p>These routines have been rewritten; the previous implementations did not
always behave exactly as specified.</p>
<h3>Shared Library Versions</h3>
<p>On architectures that can support it, the shared library version number for

View File

@@ -29,148 +29,165 @@
#include "epicsString.h"
/* Deprecated, use epicsStrnRawFromEscaped() instead */
int dbTranslateEscape(char *to, const char *from)
int dbTranslateEscape(char *dst, const char *src)
{
size_t big_enough = strlen(from)+1;
return epicsStrnRawFromEscaped(to, big_enough, from, big_enough);
size_t big_enough = strlen(src) + 1;
return epicsStrnRawFromEscaped(dst, big_enough, src, big_enough);
}
int epicsStrnRawFromEscaped(char *to, size_t outsize, const char *from,
size_t inlen)
int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src,
size_t srclen)
{
const char *pfrom = from;
char *pto = to;
char c;
size_t nto = 0, nfrom = 0;
int rem = dstlen;
int ndst = 0;
if (outsize == 0)
return 0;
while (srclen--) {
int c = *src++;
#define OUT(chr) if (--rem > 0) ndst++, *dst++ = chr
while ((c = *pfrom++) && nto < outsize && nfrom < inlen) {
nfrom++;
if (c == '\\') {
if (nfrom >= inlen || *pfrom == '\0') break;
switch (*pfrom) {
case 'a': pfrom++; nfrom++; *pto++ = '\a' ; nto++; break;
case 'b': pfrom++; nfrom++; *pto++ = '\b' ; nto++; break;
case 'f': pfrom++; nfrom++; *pto++ = '\f' ; nto++; break;
case 'n': pfrom++; nfrom++; *pto++ = '\n' ; nto++; break;
case 'r': pfrom++; nfrom++; *pto++ = '\r' ; nto++; break;
case 't': pfrom++; nfrom++; *pto++ = '\t' ; nto++; break;
case 'v': pfrom++; nfrom++; *pto++ = '\v' ; nto++; break;
case '\\': pfrom++; nfrom++; *pto++ = '\\' ; nto++; break;
case '\?': pfrom++; nfrom++; *pto++ = '\?' ; nto++; break;
case '\'': pfrom++; nfrom++; *pto++ = '\'' ; nto++; break;
case '\"': pfrom++; nfrom++; *pto++ = '\"' ; nto++; break;
case '0' :case '1' :case '2' :case '3' :
case '4' :case '5' :case '6' :case '7' :
{
int i;
char strval[4] = {0,0,0,0};
unsigned int ival;
unsigned char *pchar;
if (!c) break;
for (i=0; i<3; i++) {
if ((*pfrom < '0') || (*pfrom > '7')) break;
strval[i] = *pfrom++; nfrom++;
}
sscanf(strval,"%o",&ival);
pchar = (unsigned char *)(pto++); nto++;
*pchar = (unsigned char)(ival);
}
break;
case 'x' :
{
int i;
char strval[3] = {0,0,0};
unsigned int ival;
unsigned char *pchar;
pfrom++; /*skip the x*/
for (i=0; i<2; i++) {
if (!isxdigit((int)*pfrom)) break;
strval[i] = *pfrom++; nfrom++;
}
sscanf(strval,"%x",&ival);
pchar = (unsigned char *)(pto++); nto++;
*pchar = (unsigned char)(ival);
}
break;
default:
*pto++ = *pfrom++; nfrom++; nto++;
}
} else {
*pto++ = c; nto++;
input:
if (c != '\\') {
OUT(c);
continue;
}
}
if (nto == outsize)
pto--;
*pto = '\0';
return nto;
}
int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf,
size_t inlen)
{
int maxout = outsize;
int nout = 0;
int len;
char *outpos = outbuf;
if (!srclen-- || !(c = *src++)) break;
while (inlen--) {
char c = *inbuf++;
switch (c) {
case '\a': len = epicsSnprintf(outpos, maxout, "\\a"); break;
case '\b': len = epicsSnprintf(outpos, maxout, "\\b"); break;
case '\f': len = epicsSnprintf(outpos, maxout, "\\f"); break;
case '\n': len = epicsSnprintf(outpos, maxout, "\\n"); break;
case '\r': len = epicsSnprintf(outpos, maxout, "\\r"); break;
case '\t': len = epicsSnprintf(outpos, maxout, "\\t"); break;
case '\v': len = epicsSnprintf(outpos, maxout, "\\v"); break;
case '\\': len = epicsSnprintf(outpos, maxout, "\\\\"); ; break;
case '\'': len = epicsSnprintf(outpos, maxout, "\\'"); break;
case '\"': len = epicsSnprintf(outpos, maxout, "\\\""); break;
default:
if (isprint((int)c))
len = epicsSnprintf(outpos, maxout, "%c", c);
else
len = epicsSnprintf(outpos, maxout, "\\%03o",
(unsigned char)c);
break;
}
if (len<0) return -1;
nout += len;
if (nout < outsize) {
maxout -= len;
outpos += len;
} else {
outpos = outpos + maxout -1;
maxout = 1;
case 'a': OUT('\a'); break;
case 'b': OUT('\b'); break;
case 'f': OUT('\f'); break;
case 'n': OUT('\n'); break;
case 'r': OUT('\r'); break;
case 't': OUT('\t'); break;
case 'v': OUT('\v'); break;
case '\\': OUT('\\'); break;
case '\'': OUT('\''); break;
case '\"': OUT('\"'); break;
case '0' :case '1' :case '2' :case '3' :
case '4' :case '5' :case '6' :case '7' :
{ /* \ooo */
unsigned int u = c - '0';
if (!srclen-- || !(c = *src++)) {
OUT(u); goto done;
}
if (c < '0' || c > '7') {
OUT(u); goto input;
}
u = u << 3 | (c - '0');
if (!srclen-- || !(c = *src++)) {
OUT(u); goto done;
}
if (c < '0' || c > '7') {
OUT(u); goto input;
}
u = u << 3 | (c - '0');
if (u > 0377) {
/* Undefined behaviour! */
}
OUT(u);
}
break;
case 'x' :
{ /* \xXXX... */
unsigned int u = 0;
if (!srclen-- || !(c = *src++))
goto done;
while (isxdigit(c)) {
u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0');
if (u > 0xff) {
/* Undefined behaviour! */
}
if (!srclen-- || !(c = *src++)) {
OUT(u);
goto done;
}
}
OUT(u);
goto input;
}
default:
OUT(c);
}
#undef OUT
}
*outpos = '\0';
return nout;
done:
if (dstlen)
*dst = '\0';
return ndst;
}
size_t epicsStrnEscapedFromRawSize(const char *inbuf, size_t inlen)
int epicsStrnEscapedFromRaw(char *dst, size_t dstlen, const char *src,
size_t srclen)
{
size_t nout = inlen;
int rem = dstlen;
int ndst = 0;
while (inlen--) {
char c = *inbuf++;
if (dst == src)
return -1;
while (srclen--) {
int c = *src++;
#define OUT(chr) ndst++; if (--rem > 0) *dst++ = chr
switch (c) {
case '\a': OUT('\\'); OUT('a'); break;
case '\b': OUT('\\'); OUT('b'); break;
case '\f': OUT('\\'); OUT('f'); break;
case '\n': OUT('\\'); OUT('n'); break;
case '\r': OUT('\\'); OUT('r'); break;
case '\t': OUT('\\'); OUT('t'); break;
case '\v': OUT('\\'); OUT('v'); break;
case '\\': OUT('\\'); OUT('\\'); break;
case '\'': OUT('\\'); OUT('\''); break;
case '\"': OUT('\\'); OUT('\"'); break;
default:
if (isprint(c)) {
OUT(c);
break;
}
OUT('\\');
OUT('0' + ((c & 0300) >> 6));
OUT('0' + ((c & 0070) >> 3));
OUT('0' + (c & 0007));
}
#undef OUT
}
if (dstlen)
*dst = '\0';
return ndst;
}
size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen)
{
size_t ndst = srclen;
while (srclen--) {
int c = *src++;
switch (c) {
case '\a': case '\b': case '\f': case '\n':
case '\r': case '\t': case '\v': case '\\':
case '\'': case '\"':
nout++;
ndst++;
break;
default:
if (!isprint((int)c))
nout += 3;
if (!isprint(c))
ndst += 3;
}
}
return nout;
return ndst;
}
int epicsStrCaseCmp(const char *s1, const char *s2)

View File

@@ -88,7 +88,7 @@ MAIN(epicsStringTest)
char *s;
int status;
testPlan(323);
testPlan(402);
testChars();
@@ -125,41 +125,199 @@ MAIN(epicsStringTest)
testGlob();
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 0, ABCD, 4);
testOk(status == 4, "epicsStrnEscapedFromRaw(out, 0) -> %d (exp. 4)", status);
testOk(result[0] == 'x', " No output");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 0, ABCD, 4);
testOk(status == 0, "epicsStrnRawFromEscaped(out, 0) -> %d (exp. 0)", status);
testOk(result[0] == 'x', " No output");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 1, ABCD, 4);
testOk(status == 4, "epicsStrnEscapedFromRaw(out, 1) -> %d (exp. 4)", status);
testOk(result[0] == 0, " 0-terminated");
testOk(result[1] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 1, ABCD, 4);
testOk(status == 0, "epicsStrnRawFromEscaped(out, 1) -> %d (exp. 0)", status);
testOk(result[0] == 0, " 0-terminated");
testOk(result[1] == 'x', " No overrun");
testDiag("Testing size = epicsStrnEscapedFromRawSize");
status = epicsStrnEscapedFromRawSize(ABCD, 3);
testOk(status == 3, "size(\"ABCD\", 3) -> %d (exp. 3)", status);
status = epicsStrnEscapedFromRawSize(ABCD, 4);
testOk(status == 4, "size(\"ABCD\", 4) -> %d (exp. 4)", status);
status = epicsStrnEscapedFromRawSize(ABCD, 5);
testOk(status == 8, "size(\"ABCD\", 5) -> %d (exp. 8)", status);
testDiag("Testing esc = epicsStrnEscapedFromRaw(out, 4, ...)");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 3);
testOk(status == 3, "epicsStrnEscapedFromRaw returned %d (exp. 3)", status);
testOk(result[4] == 'x', "epicsStrnEscapedFromRaw no buffer overrun");
testOk(result[3] == 0, "epicsStrnEscapedFromRaw 0-terminated");
testOk(status == 3, "esc(\"ABCD\", 3) -> %d (exp. 3)", status);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 4);
testOk(status == 4, "epicsStrnEscapedFromRaw returned %d (exp. 4)", status);
testOk(result[4] == 'x', "epicsStrnEscapedFromRaw no buffer overrun");
testOk(result[3] == 0, "epicsStrnEscapedFromRaw 0-terminated");
testOk(status == 4, "esc(\"ABCD\", 4) -> %d (exp. 4)", status);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCDE, 5);
testOk(status == 5, "epicsStrnEscapedFromRaw returned %d (exp. 5)", status);
testOk(result[4] == 'x', "epicsStrnEscapedFromRaw no buffer overrun");
testOk(result[3] == 0, "epicsStrnEscapedFromRaw 0-terminated");
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 5);
testOk(status == 8, "esc(\"ABCD\", 5) -> %d (exp. 8)", status);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No overrun");
testDiag("Testing raw = epicsStrnRawFromEscaped(out, 4, ...)");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 0);
testOk(status == 0, "raw(\"ABCD\", 0) -> %d (exp. 0)", status);
testOk(result[0] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 1);
testOk(status == 1, "raw(\"ABCD\", 1) -> %d (exp. 1)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 2);
testOk(status == 2, "raw(\"ABCD\", 2) -> %d (exp. 2)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 3);
testOk(status == 3, "epicsStrnRawFromEscaped returned %d (exp. 3)", status);
testOk(result[4] == 'x', "epicsStrnRawFromEscaped no buffer overrun");
testOk(result[3] == 0, "epicsStrnRawFromEscaped 0-terminated");
testOk(status == 3, "raw(\"ABCD\", 3) -> %d (exp. 3)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 4);
testOk(status == 4, "epicsStrnRawFromEscaped returned %d (exp. 4)", status);
testOk(result[4] == 'x', "epicsStrnRawFromEscaped no buffer overrun");
testOk(result[3] == 0, "epicsStrnRawFromEscaped 0-terminated");
testOk(status == 3, "raw(\"ABCD\", 4) -> %d (exp. 3)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCDE, 5);
testOk(status == 4, "epicsStrnRawFromEscaped returned %d (exp. 4)", status);
testOk(result[4] == 'x', "epicsStrnRawFromEscaped no buffer overrun");
testOk(result[3] == 0, "epicsStrnRawFromEscaped 0-terminated");
testOk(status == 3, "raw(\"ABCDE\", 5) -> %d (exp. 3)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
testOk(result[3] == 0, " 0-terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "A", 2);
testOk(status == 1, "raw(\"A\", 2) -> %d (exp. 1)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\123", 1);
testOk(status == 0, "raw(\"\\123\", 1) -> %d (exp. 0)", status);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\123", 2);
testOk(status == 1, "raw(\"\\123\", 2) -> %d (exp. 1)", status);
testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\123", 3);
testOk(status == 1, "raw(\"\\123\", 3) -> %d (exp. 1)", status);
testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\123", 4);
testOk(status == 1, "raw(\"\\123\", 4) -> %d (exp. 1)", status);
testOk(result[0] == 0123, " Octal escape (got \\%03o)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\812", 2);
testOk(status == 1, "raw(\"\\812\", 2) -> %d (exp. 1)", status);
testOk(result[0] == '8', " Escaped '%c')", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\182", 3);
testOk(status == 2, "raw(\"\\182\", 3) -> %d (exp. 2)", status);
testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]);
testOk(result[1] == '8', " Terminated with '%c'", result[1]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\128", 4);
testOk(status == 2, "raw(\"\\128\", 4) -> %d (exp. 2)", status);
testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]);
testOk(result[1] == '8', " Terminator char got '%c'", result[1]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 1);
testOk(status == 0, "raw(\"\\x12\", 1) -> %d (exp. 0)", status);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 2);
testOk(status == 0, "raw(\"\\x12\", 2) -> %d (exp. 0)", status);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 3);
testOk(status == 1, "raw(\"\\x12\", 3) -> %d (exp. 1)", status);
testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 4);
testOk(status == 1, "raw(\"\\x12\", 4) -> %d (exp. 1)", status);
testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\xaF", 4);
testOk(status == 1, "raw(\"\\xaF\", 4) -> %d (exp. 1)", status);
testOk(result[0] == '\xaF'," Hex escape (got \\x%x)", result[0] & 0xFF);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x012", 5);
testOk(status == 1, "raw(\"\\x012\", 5) -> %d (exp. 1)", status);
testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x0012", 6);
testOk(status == 1, "raw(\"\\x0012\", 6) -> %d (exp. 1)", status);
testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]);
testOk(result[status] == 0, " 0-terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x1g", 4);
testOk(status == 2, "raw(\"\\x1g\", 4) -> %d (exp. 2)", status);
testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]);
testOk(result[1] == 'g', " Terminator char got '%c'", result[1]);
testOk(result[status] == 0, " 0-terminated");
return testDone();
}