#ifndef _EZPWD_OUTPUT #define _EZPWD_OUTPUT #include #include #include #include #include #include #include #include #include // // ezpwd::hexchr -- escape/hexify char c, output using func/meth f, in width w >= 2 // ezpwd::hexify -- hexify something that can be converted to an unsigned char // ezpwd::hexout -- hexify each element in the range (beg,end], limited by stream's width // // std::ostream << ezpwd::hexify( c ) // output any char escaped/hex // std::ostream << ezpwd::hexout( beg, end ) // output any char iterator to ostream // std::ostream << std::vector // std::ostream << std::array // ezpwd::hexchr( c, [](unsigned char c){...;} )// output escaped/hex char via functor // ezpwd::hexout( beg, end, FILE* ) // output any char iterator to FILE* // // Output unprintable unsigned char data in hex, escape printable/whitespace data. // namespace ezpwd { struct hexify { unsigned char c; std::streamsize w; explicit hexify( unsigned char _c, std::streamsize _w = 2 ) : c( _c ) , w( _w ) { ; } explicit hexify( char _c, std::streamsize _w = 2 ) : c( (unsigned char)_c ) , w( _w ) { ; } }; struct hexstr { const std::string &s; explicit hexstr( const std::string &_s ) : s( _s ) { ; } }; template // a functor taking a char void hexchr( unsigned char c, F f = []( unsigned char c ) { std::cout.put( c );}, size_t w = 2 ) { for ( ; w > 2; --w ) f( ' ' ); if ( std::isprint( c ) || std::isspace( c ) || c == '\0' || c == '\a' || c == '\b' || c == 0x1B ) { // '\e' is not standard switch ( c ) { case 0x00: f( '\\' ); f( '0' ); break; // NUL case 0x07: f( '\\' ); f( 'a' ); break; // BEL case 0x08: f( '\\' ); f( 'b' ); break; // BS case 0x09: f( '\\' ); f( 't' ); break; // HT case 0x0A: f( '\\' ); f( 'n' ); break; // LF case 0x0B: f( '\\' ); f( 'v' ); break; // VT case 0x0C: f( '\\' ); f( 'f' ); break; // FF case 0x0D: f( '\\' ); f( 'r' ); break; // CR case 0x1B: f( '\\' ); f( 'e' ); break; // ESC case '\"': f( '\\' ); f( '"' ); break; // " case '\'': f( '\\' ); f( '\''); break; // ' case '\\': f( '\\' ); f( '\\'); break; // '\' default: f( ' ' ); f( c ); // space, any other printable character } } else { f( "0123456789ABCDEF"[( c >> 4 ) & 0x0f ] ); f( "0123456789ABCDEF"[( c >> 0 ) & 0x0f ] ); } } inline std::ostream &operator<<( std::ostream &lhs, const ezpwd::hexify&rhs ) { ezpwd::hexchr( rhs.c, [ &lhs ]( unsigned char c ) { lhs.put( c ); }, rhs.w ); return lhs; } template < typename iter_t > inline std::ostream &hexout( std::ostream &lhs, const iter_t &beg, const iter_t &end ) { std::streamsize wid = lhs.width( 0 ); int col = 0; for ( auto i = beg; i != end; ++i ) { if ( wid && col == wid ) { lhs << std::endl; col = 0; } lhs << hexify( *i ); ++col; } return lhs; } template < typename iter_t > inline std::FILE *hexout( const iter_t &beg, const iter_t &end, std::FILE *lhs ) { for ( auto i = beg; i != end; ++i ) { ezpwd::hexchr( *i, [ lhs ]( unsigned char c ) { std::fputc( c, lhs ); } ); } return lhs; } inline std::ostream &operator<<( std::ostream &lhs, const ezpwd::hexstr&rhs ) { return ezpwd::hexout( lhs, rhs.s.begin(), rhs.s.end() ); } } // namespace ezpwd namespace std { template < size_t S > inline std::ostream &operator<<( std::ostream &lhs, const std::array &rhs ) { return ezpwd::hexout( lhs, rhs.begin(), rhs.end() ); } inline std::ostream &operator<<( std::ostream &lhs, const std::vector &rhs ) { return ezpwd::hexout( lhs, rhs.begin(), rhs.end() ); } // // << pair // << set -- sorted by T // << map -- sorted by T (key) // << vector // // Handle output of various container types. // // Output pairs and sets of pairs, respecting specified widths (as appropriate). For example // a set of pairs of integeters 's', if output as "... << std::setw( 13 ) << s;", would yield: // // ( 1, 2) ( 3, 4) ... // template std::ostream &operator<<( std::ostream &lhs, const std::pair &rhs ) { std::streamsize w = std::max( std::streamsize( 0 ), std::streamsize( lhs.width() - 3 )); lhs << std::setw( 0 ) << '(' << std::setw( w / 2 ) << rhs.first << ',' << std::setw( w - w / 2 ) << rhs.second << ')'; return lhs; } template std::ostream &operator<<( std::ostream &lhs, const std::set &rhs ) { std::streamsize w = lhs.width(); // If width is set, use if for each item for ( typename std::set::const_iterator si = rhs.begin() ; si != rhs.end() ; ++si ) { if ( si != rhs.begin()) lhs << ' '; lhs << std::setw( w ) << *si; } lhs << std::setw( 0 ); // If container empty, must clear return lhs; } template std::ostream &operator<<( std::ostream &lhs, const std::map&rhs ) { std::streamsize w = lhs.width(); // If width is set, use if for each item std::vector key; for ( typename std::map::const_iterator mi = rhs.begin() ; mi != rhs.end() ; ++mi ) key.push_back( mi->first ); std::sort( key.begin(), key.end() ); for ( typename std::vector::const_iterator ki = key.begin() ; ki != key.end() ; ++ki ) { if ( ki != key.begin()) lhs << ' '; lhs << std::setw( w ) << *rhs.find( *ki ); } lhs << std::setw( 0 ); // If container empty, must clear return lhs; } template std::ostream &operator<<( std::ostream &lhs, const std::vector &rhs ) { for( size_t i = 0; i < rhs.size(); ++i ) { if ( i ) lhs << ", "; lhs << rhs[i]; } return lhs; } } // namespace std // // ezpwd::buf_t -- describe a C string buffer, to allow C++ output operations // ezpwd::streambuf_to_buf_t -- output charcters, always NUL terminated // // << ... -- Copy the into the C buffer, always NUL terminating // // Copies contents into buffer, and always NUL-terminates. Returns advanced buf_t (NOT // including the terminating NUL, suitable for repeating ... << operations. // // std::ostream( & ) << ... // // Use standard ostream operations to send output to a C buffer, always NUL // terminating, and never exceeding capacity. // namespace ezpwd { typedef std::pair buf_t; class streambuf_to_buffer : public std::streambuf { private: char *_buf; size_t _siz; public: // // streambuf_to_buf_t -- remember buf_t details // ~streambuf_to_buf_t -- no virtual destructor required; nothing to clean up // streambuf_to_buffer( char *buf, size_t siz ) : _buf( buf ) , _siz( siz ) { if ( _siz > 0 ) *_buf = 0; } explicit streambuf_to_buffer( const buf_t &buf ) : streambuf_to_buffer( buf.first, buf.second ) { ; } // // overflow -- Append c, always NUL terminating // virtual int overflow( int c ) { if ( _siz <= 1 ) return EOF; // No room for c and NUL; EOF if ( EOF == c ) return 0; // EOF provided; do nothing --_siz; *_buf++ = char( c ); *_buf = 0; return c; } }; // class streambuf_to_buffer } // namespace ezpwd namespace std { inline ezpwd::buf_t operator<<( const ezpwd::buf_t &buf, const std::string &str ) { if ( buf.first && str.size() + 1 <= buf.second ) { std::copy( str.begin(), str.end(), buf.first ); buf.first[str.size()] = 0; return ezpwd::buf_t( buf.first + str.size(), buf.second - str.size() ); } else if ( buf.first && buf.second ) { std::copy( str.begin(), str.begin() + buf.second - 1, buf.first ); buf.first[buf.second-1] = 0; return ezpwd::buf_t( buf.first + buf.second - 1, 1 ); } return buf; // NULL pointer or 0 size. } // // << ... // // Useful (but inefficient) standard output formatting directly to a std::string. Use only for // testing code, for efficiency reasons... // template < typename T > inline std::string operator<<( const std::string &lhs, const T &rhs ) { std::ostringstream oss; oss << rhs; return std::string( lhs ).append( oss.str() ); } } // namespace std #endif // _EZPWD_OUTPUT