helper to escape while printing strings

This commit is contained in:
Michael Davidsaver
2018-10-07 09:30:53 -07:00
parent 818fce324c
commit fa731bf6c3
3 changed files with 115 additions and 13 deletions

View File

@@ -5,6 +5,7 @@
*/
#include <stdio.h>
#include <ctype.h>
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#define HAVE_ISATTY
@@ -13,6 +14,7 @@
#include <deque>
#include <epicsTime.h>
#include <epicsString.h>
#define epicsExportSharedSymbols
#include <pv/bitSet.h>
@@ -436,4 +438,57 @@ std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& forma
return strm;
}
static char hexdigit(char c) {
c &= 0xf;
if(c<9)
return '0'+c;
else
return 'A'+c-10;
}
escape::~escape() {}
std::string escape::str() const
{
std::ostringstream strm;
strm<<(*this);
return strm.str();
}
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q)
{
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
const char C = Q.orig[pos];
char quote = '\\', next;
// compare me with epicsStrnEscapedFromRaw()
switch(C) {
case '\a': next = 'a'; break;
case '\b': next = 'b'; break;
case '\f': next = 'f'; break;
case '\n': next = 'n'; break;
case '\r': next = 'r'; break;
case '\t': next = 't'; break;
case '\v': next = 'v'; break;
case '\\': next = '\\'; break;
case '\'': next = '\''; break;
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
default:
if(!isprint(C)) {
// print three charator escape
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
} else {
// literal
strm.put(C);
}
continue;
}
// print two charactor escape
strm.put(quote);
strm.put(next);
}
return strm;
}
}} //epics::pvData

View File

@@ -208,6 +208,35 @@ static FORCE_INLINE
typename detail::print_convolute<T>::return_t
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
class escape;
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
//! Helper to print a string with escaping
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
struct epicsShareClass escape
{
enum style_t {
C, // default
CSV, // a la RFC4180
};
private:
const std::string& orig;
style_t S;
public:
escape(const std::string& orig) :orig(orig), S(C) {}
~escape();
//! Change escaping style
inline escape& style(style_t s) { S = s; return *this; }
//! print to string and return. (alloc and copy)
std::string str() const;
friend
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
};
}} // end namespace
#endif // PVTYPECAST_H

View File

@@ -26,6 +26,16 @@ typedef std::vector<std::string> lines_t;
namespace {
struct SB {
std::ostringstream strm;
operator std::string() { return strm.str(); }
template<typename T>
SB& operator<<(const T& v) {
strm<<v;
return *this;
}
};
lines_t lines(const std::string& str)
{
lines_t ret;
@@ -41,13 +51,6 @@ lines_t lines(const std::string& str)
return ret;
}
std::string escape(const std::string& inp) {
size_t N = epicsStrnEscapedFromRawSize(inp.c_str(), inp.size());
std::vector<char> esc(N+1, '\0'); // include space for trailing nil
epicsStrnEscapedFromRaw(&esc[0], esc.size(), inp.c_str(), inp.size());
return std::string(&esc[0]);
}
std::string print(const pvd::PVStructure::Formatter& fmt)
{
std::ostringstream strm;
@@ -74,7 +77,7 @@ testDiff(const std::string& expect, const std::string& actual, const std::string
while(L<lhs.size() && R<rhs.size()) {
if(lhs[L]==rhs[R]) {
ret<<" "<<escape(lhs[L])<<'\n';
ret<<" "<<pvd::escape(lhs[L])<<'\n';
L++;
R++;
@@ -100,9 +103,9 @@ testDiff(const std::string& expect, const std::string& actual, const std::string
}
for(size_t l=L; l<Lp; l++)
ret<<"- "<<escape(lhs[l])<<'\n';
ret<<"- "<<pvd::escape(lhs[l])<<'\n';
for(size_t r=R; r<Rp; r++)
ret<<"+ "<<escape(rhs[r])<<'\n';
ret<<"+ "<<pvd::escape(rhs[r])<<'\n';
L = Lp;
R = Rp;
// loop around and print matching line
@@ -110,9 +113,9 @@ testDiff(const std::string& expect, const std::string& actual, const std::string
}
for(; L<lhs.size(); L++)
ret<<"- "<<escape(lhs[L])<<'\n';
ret<<"- "<<pvd::escape(lhs[L])<<'\n';
for(; R<rhs.size(); R++)
ret<<"+ "<<escape(rhs[R])<<'\n';
ret<<"+ "<<pvd::escape(rhs[R])<<'\n';
return ret;
}
@@ -229,13 +232,28 @@ void testRaw()
));
}
void testEscape()
{
testDiag("%s", CURRENT_FUNCTION);
testEqual("hello world", std::string(SB()<<pvd::escape("hello world")));
testEqual("hello\\nworld", std::string(SB()<<pvd::escape("hello\nworld")));
testEqual("hello\\\"world", std::string(SB()<<pvd::escape("hello\"world")));
testEqual("hello\\x7Fworld", std::string(SB()<<pvd::escape("hello\x7Fworld")));
testEqual("hello\"\"world", std::string(SB()<<pvd::escape("hello\"world").style(pvd::escape::CSV)));
testEqual("hello\"\"world", pvd::escape("hello\"world").style(pvd::escape::CSV).str());
}
} // namespace
MAIN(testprinter)
{
testPlan(0);
testPlan(14);
showNTScalarNumeric();
showNTScalarString();
testRaw();
testEscape();
return testDone();
}