diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 239d3b673..5fab30c35 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -14,6 +14,11 @@
These routines have been rewritten; the previous implementations did not +always behave exactly as specified.
+On architectures that can support it, the shared library version number for diff --git a/src/libCom/misc/epicsString.c b/src/libCom/misc/epicsString.c index ddf8634aa..de38005b4 100644 --- a/src/libCom/misc/epicsString.c +++ b/src/libCom/misc/epicsString.c @@ -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) diff --git a/src/libCom/test/epicsStringTest.c b/src/libCom/test/epicsStringTest.c index 36f8233b5..a5fffaf49 100644 --- a/src/libCom/test/epicsStringTest.c +++ b/src/libCom/test/epicsStringTest.c @@ -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(); }