345 lines
9.1 KiB
Plaintext
345 lines
9.1 KiB
Plaintext
#ifndef _EZPWD_OUTPUT
|
|
#define _EZPWD_OUTPUT
|
|
|
|
#include <cstdio>
|
|
#include <streambuf>
|
|
#include <iostream>
|
|
#include <cctype>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <set>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
//
|
|
// 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<unsigend char>
|
|
// std::ostream << std::array<unsigend char, N>
|
|
// 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 <typename F> // 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<unsigned char,S>
|
|
&rhs )
|
|
{
|
|
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
|
|
}
|
|
|
|
inline
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const std::vector<unsigned char>
|
|
&rhs )
|
|
{
|
|
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
|
|
}
|
|
|
|
//
|
|
// <ostream&> << pair<T,U>
|
|
// <ostream&> << set<T> -- sorted by T
|
|
// <ostream&> << map<T,U> -- sorted by T (key)
|
|
// <ostream&> << vector<T>
|
|
//
|
|
// 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 <class T, class U>
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const std::pair<T,U> &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 <class T>
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const std::set<T> &rhs )
|
|
{
|
|
std::streamsize w = lhs.width(); // If width is set, use if for each item
|
|
for ( typename std::set<T>::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 <class T, class U>
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const std::map<T,U>&rhs )
|
|
{
|
|
std::streamsize w = lhs.width(); // If width is set, use if for each item
|
|
std::vector<T> key;
|
|
for ( typename std::map<T,U>::const_iterator
|
|
mi = rhs.begin()
|
|
; mi != rhs.end()
|
|
; ++mi )
|
|
key.push_back( mi->first );
|
|
std::sort( key.begin(), key.end() );
|
|
for ( typename std::vector<T>::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 <class T>
|
|
std::ostream &operator<<(
|
|
std::ostream &lhs,
|
|
const std::vector<T> &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
|
|
//
|
|
// <buf_t> << ... -- Copy the <string> into the C <char*,size_t> buffer, always NUL terminating
|
|
//
|
|
// Copies <string> contents into buffer, and always NUL-terminates. Returns advanced buf_t (NOT
|
|
// including the terminating NUL, suitable for repeating ... << <string> operations.
|
|
//
|
|
// std::ostream( &<streambuf_to_buf_t> ) << ...
|
|
//
|
|
// Use standard ostream operations to send output to a C buffer, always NUL
|
|
// terminating, and never exceeding capacity.
|
|
//
|
|
namespace ezpwd {
|
|
|
|
typedef std::pair<char *,size_t>
|
|
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.
|
|
}
|
|
|
|
|
|
//
|
|
// <std::string> << ...
|
|
//
|
|
// 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
|