helper to escape while printing strings
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user