clang-format Mosh

Run clang-format over the Mosh source tree. This is a large change and
has been factored into its own commit for auditability. Reproduce it
with

    find . -name \*.cc -or -name \*.h | while read f; do clang-format -i --style=file $f; done
This commit is contained in:
Benjamin Barenblat
2023-08-07 21:53:48 -04:00
committed by Alex Chernyakhovsky
parent 0b15dc94fa
commit 3acaa1c4d3
77 changed files with 4838 additions and 4848 deletions
+24 -28
View File
@@ -32,14 +32,14 @@ extern "C" {
/* Return status codes: Negative return values indicate an error occurred. /* Return status codes: Negative return values indicate an error occurred.
* For full explanations of error values, consult the implementation's * For full explanations of error values, consult the implementation's
* documentation. */ * documentation. */
#define AE_SUCCESS ( 0) /* Indicates successful completion of call */ #define AE_SUCCESS ( 0 ) /* Indicates successful completion of call */
#define AE_INVALID (-1) /* Indicates bad tag during decryption */ #define AE_INVALID ( -1 ) /* Indicates bad tag during decryption */
#define AE_NOT_SUPPORTED (-2) /* Indicates unsupported option requested */ #define AE_NOT_SUPPORTED ( -2 ) /* Indicates unsupported option requested */
/* Flags: When data can be processed "incrementally", these flags are used /* Flags: When data can be processed "incrementally", these flags are used
* to indicate whether the submitted data is the last or not. */ * to indicate whether the submitted data is the last or not. */
#define AE_FINALIZE (1) /* This is the last of data */ #define AE_FINALIZE ( 1 ) /* This is the last of data */
#define AE_PENDING (0) /* More data of is coming */ #define AE_PENDING ( 0 ) /* More data of is coming */
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
@@ -55,10 +55,10 @@ typedef struct _ae_ctx ae_ctx;
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
ae_ctx* ae_allocate (void *misc); /* Allocate ae_ctx, set optional ptr */ ae_ctx* ae_allocate( void* misc ); /* Allocate ae_ctx, set optional ptr */
void ae_free (ae_ctx *ctx); /* Deallocate ae_ctx struct */ void ae_free( ae_ctx* ctx ); /* Deallocate ae_ctx struct */
int ae_clear (ae_ctx *ctx); /* Undo initialization */ int ae_clear( ae_ctx* ctx ); /* Undo initialization */
int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */ int ae_ctx_sizeof( void ); /* Return sizeof(ae_ctx) */
/* ae_allocate() allocates an ae_ctx structure, but does not initialize it. /* ae_allocate() allocates an ae_ctx structure, but does not initialize it.
* ae_free() deallocates an ae_ctx structure, but does not zeroize it. * ae_free() deallocates an ae_ctx structure, but does not zeroize it.
* ae_clear() zeroes sensitive values associated with an ae_ctx structure * ae_clear() zeroes sensitive values associated with an ae_ctx structure
@@ -72,11 +72,7 @@ int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_init(ae_ctx *ctx, int ae_init( ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len );
const void *key,
int key_len,
int nonce_len,
int tag_len);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Initialize an ae_ctx context structure. * Initialize an ae_ctx context structure.
@@ -95,15 +91,15 @@ int ae_init(ae_ctx *ctx,
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_encrypt(ae_ctx *ctx, int ae_encrypt( ae_ctx* ctx,
const void *nonce, const void* nonce,
const void *pt, const void* pt,
int pt_len, int pt_len,
const void *ad, const void* ad,
int ad_len, int ad_len,
void *ct, void* ct,
void *tag, void* tag,
int final); int final );
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Encrypt plaintext; provide for authentication of ciphertext/associated data. * Encrypt plaintext; provide for authentication of ciphertext/associated data.
@@ -132,15 +128,15 @@ int ae_encrypt(ae_ctx *ctx,
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_decrypt(ae_ctx *ctx, int ae_decrypt( ae_ctx* ctx,
const void *nonce, const void* nonce,
const void *ct, const void* ct,
int ct_len, int ct_len,
const void *ad, const void* ad,
int ad_len, int ad_len,
void *pt, void* pt,
const void *tag, const void* tag,
int final); int final );
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Decrypt ciphertext; provide authenticity of plaintext and associated data. * Decrypt ciphertext; provide authenticity of plaintext and associated data.
+17 -19
View File
@@ -33,8 +33,8 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "src/util/fatal_assert.h"
#include "src/crypto/base64.h" #include "src/crypto/base64.h"
#include "src/util/fatal_assert.h"
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -60,27 +60,26 @@ static const unsigned char reverse[] = {
}; };
/* Reverse maps from an ASCII char to a base64 sixbit value. Returns > 0x3f on failure. */ /* Reverse maps from an ASCII char to a base64 sixbit value. Returns > 0x3f on failure. */
static unsigned char base64_char_to_sixbit(unsigned char c) static unsigned char base64_char_to_sixbit( unsigned char c )
{ {
return reverse[c]; return reverse[c];
} }
bool base64_decode( const char *b64, const size_t b64_len, bool base64_decode( const char* b64, const size_t b64_len, uint8_t* raw, size_t* raw_len )
uint8_t *raw, size_t *raw_len )
{ {
fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */
fatal_assert( *raw_len == 16 ); fatal_assert( *raw_len == 16 );
uint32_t bytes = 0; uint32_t bytes = 0;
for (int i = 0; i < 22; i++) { for ( int i = 0; i < 22; i++ ) {
unsigned char sixbit = base64_char_to_sixbit(*(b64++)); unsigned char sixbit = base64_char_to_sixbit( *( b64++ ) );
if (sixbit > 0x3f) { if ( sixbit > 0x3f ) {
return false; return false;
} }
bytes <<= 6; bytes <<= 6;
bytes |= sixbit; bytes |= sixbit;
/* write groups of 3 */ /* write groups of 3 */
if (i % 4 == 3) { if ( i % 4 == 3 ) {
raw[0] = bytes >> 16; raw[0] = bytes >> 16;
raw[1] = bytes >> 8; raw[1] = bytes >> 8;
raw[2] = bytes; raw[2] = bytes;
@@ -90,33 +89,32 @@ bool base64_decode( const char *b64, const size_t b64_len,
} }
/* last byte of output */ /* last byte of output */
*raw = bytes >> 4; *raw = bytes >> 4;
if (b64[0] != '=' || b64[1] != '=') { if ( b64[0] != '=' || b64[1] != '=' ) {
return false; return false;
} }
return true; return true;
} }
void base64_encode( const uint8_t *raw, const size_t raw_len, void base64_encode( const uint8_t* raw, const size_t raw_len, char* b64, const size_t b64_len )
char *b64, const size_t b64_len )
{ {
fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
/* first 15 bytes of input */ /* first 15 bytes of input */
for (int i = 0; i < 5; i++) { for ( int i = 0; i < 5; i++ ) {
uint32_t bytes = (raw[0] << 16) | (raw[1] << 8) | raw[2]; uint32_t bytes = ( raw[0] << 16 ) | ( raw[1] << 8 ) | raw[2];
b64[0] = table[(bytes >> 18) & 0x3f]; b64[0] = table[( bytes >> 18 ) & 0x3f];
b64[1] = table[(bytes >> 12) & 0x3f]; b64[1] = table[( bytes >> 12 ) & 0x3f];
b64[2] = table[(bytes >> 6) & 0x3f]; b64[2] = table[( bytes >> 6 ) & 0x3f];
b64[3] = table[(bytes) & 0x3f]; b64[3] = table[(bytes)&0x3f];
raw += 3; raw += 3;
b64 += 4; b64 += 4;
} }
/* last byte of input, last 4 of output */ /* last byte of input, last 4 of output */
uint8_t lastchar = *raw; uint8_t lastchar = *raw;
b64[0] = table[(lastchar >> 2) & 0x3f]; b64[0] = table[( lastchar >> 2 ) & 0x3f];
b64[1] = table[(lastchar << 4) & 0x3f]; b64[1] = table[( lastchar << 4 ) & 0x3f];
b64[2] = '='; b64[2] = '=';
b64[3] = '='; b64[3] = '=';
} }
+2 -4
View File
@@ -32,8 +32,6 @@
#include <cstdint> #include <cstdint>
bool base64_decode( const char *b64, const size_t b64_len, bool base64_decode( const char* b64, const size_t b64_len, uint8_t* raw, size_t* raw_len );
uint8_t *raw, size_t *raw_len );
void base64_encode( const uint8_t *raw, const size_t raw_len, void base64_encode( const uint8_t* raw, const size_t raw_len, char* b64, const size_t b64_len );
char *b64, const size_t b64_len );
+34 -39
View File
@@ -37,12 +37,12 @@
#if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64 #if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64
# if defined(HAVE_ENDIAN_H) #if defined( HAVE_ENDIAN_H )
# include <endian.h> #include <endian.h>
# elif defined(HAVE_SYS_ENDIAN_H) #elif defined( HAVE_SYS_ENDIAN_H )
# include <sys/types.h> #include <sys/endian.h>
# include <sys/endian.h> #include <sys/types.h>
# endif #endif
#if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64 #if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64
#define be64toh betoh64 #define be64toh betoh64
@@ -50,11 +50,11 @@
#endif #endif
#elif HAVE_OSX_SWAP #elif HAVE_OSX_SWAP
# include <libkern/OSByteOrder.h> #include <libkern/OSByteOrder.h>
# define htobe64 OSSwapHostToBigInt64 #define htobe64 OSSwapHostToBigInt64
# define be64toh OSSwapBigToHostInt64 #define be64toh OSSwapBigToHostInt64
# define htobe16 OSSwapHostToBigInt16 #define htobe16 OSSwapHostToBigInt16
# define be16toh OSSwapBigToHostInt16 #define be16toh OSSwapBigToHostInt16
#else #else
@@ -70,60 +70,55 @@
/* Use unions rather than casts, to comply with strict aliasing rules. */ /* Use unions rather than casts, to comply with strict aliasing rules. */
inline uint64_t htobe64( uint64_t x ) { inline uint64_t htobe64( uint64_t x )
uint8_t xs[ 8 ] = { {
static_cast<uint8_t>( ( x >> 56 ) & 0xFF ), uint8_t xs[8] = { static_cast<uint8_t>( ( x >> 56 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 48 ) & 0xFF ), static_cast<uint8_t>( ( x >> 48 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 40 ) & 0xFF ), static_cast<uint8_t>( ( x >> 40 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 32 ) & 0xFF ), static_cast<uint8_t>( ( x >> 32 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 24 ) & 0xFF ), static_cast<uint8_t>( ( x >> 24 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 16 ) & 0xFF ), static_cast<uint8_t>( ( x >> 16 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), static_cast<uint8_t>( ( x >> 8 ) & 0xFF ),
static_cast<uint8_t>( ( x ) & 0xFF ) }; static_cast<uint8_t>( (x)&0xFF ) };
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint64_t *p64; const uint64_t* p64;
} u; } u;
u.p8 = xs; u.p8 = xs;
return *u.p64; return *u.p64;
} }
inline uint64_t be64toh( uint64_t x ) { inline uint64_t be64toh( uint64_t x )
{
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint64_t *p64; const uint64_t* p64;
} u; } u;
u.p64 = &x; u.p64 = &x;
return ( uint64_t( u.p8[ 0 ] ) << 56 ) return ( uint64_t( u.p8[0] ) << 56 ) | ( uint64_t( u.p8[1] ) << 48 ) | ( uint64_t( u.p8[2] ) << 40 )
| ( uint64_t( u.p8[ 1 ] ) << 48 ) | ( uint64_t( u.p8[3] ) << 32 ) | ( uint64_t( u.p8[4] ) << 24 ) | ( uint64_t( u.p8[5] ) << 16 )
| ( uint64_t( u.p8[ 2 ] ) << 40 ) | ( uint64_t( u.p8[6] ) << 8 ) | ( uint64_t( u.p8[7] ) );
| ( uint64_t( u.p8[ 3 ] ) << 32 )
| ( uint64_t( u.p8[ 4 ] ) << 24 )
| ( uint64_t( u.p8[ 5 ] ) << 16 )
| ( uint64_t( u.p8[ 6 ] ) << 8 )
| ( uint64_t( u.p8[ 7 ] ) );
} }
inline uint16_t htobe16( uint16_t x ) { inline uint16_t htobe16( uint16_t x )
uint8_t xs[ 2 ] = { {
static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), uint8_t xs[2] = { static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), static_cast<uint8_t>( (x)&0xFF ) };
static_cast<uint8_t>( ( x ) & 0xFF ) };
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint16_t *p16; const uint16_t* p16;
} u; } u;
u.p8 = xs; u.p8 = xs;
return *u.p16; return *u.p16;
} }
inline uint16_t be16toh( uint16_t x ) { inline uint16_t be16toh( uint16_t x )
{
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint16_t *p16; const uint16_t* p16;
} u; } u;
u.p16 = &x; u.p16 = &x;
return ( uint16_t( u.p8[ 0 ] ) << 8 ) return ( uint16_t( u.p8[0] ) << 8 ) | ( uint16_t( u.p8[1] ) );
| ( uint16_t( u.p8[ 1 ] ) );
} }
#endif #endif
+30 -33
View File
@@ -39,23 +39,22 @@
#include <sys/resource.h> #include <sys/resource.h>
#include "src/crypto/base64.h"
#include "src/crypto/byteorder.h" #include "src/crypto/byteorder.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/crypto/base64.h"
#include "src/util/fatal_assert.h"
#include "src/crypto/prng.h" #include "src/crypto/prng.h"
#include "src/util/fatal_assert.h"
using namespace Crypto; using namespace Crypto;
long int myatoi( const char *str ) long int myatoi( const char* str )
{ {
char *end; char* end;
errno = 0; errno = 0;
long int ret = strtol( str, &end, 10 ); long int ret = strtol( str, &end, 10 );
if ( ( errno != 0 ) if ( ( errno != 0 ) || ( end != str + strlen( str ) ) ) {
|| ( end != str + strlen( str ) ) ) {
throw CryptoException( "Bad integer." ); throw CryptoException( "Bad integer." );
} }
@@ -72,16 +71,14 @@ uint64_t Crypto::unique( void )
return rv; return rv;
} }
AlignedBuffer::AlignedBuffer( size_t len, const char *data ) AlignedBuffer::AlignedBuffer( size_t len, const char* data ) : m_len( len ), m_allocated( NULL ), m_data( NULL )
: m_len( len ), m_allocated( NULL ), m_data( NULL )
{ {
size_t alloc_len = len ? len : 1; size_t alloc_len = len ? len : 1;
#if defined(HAVE_POSIX_MEMALIGN) #if defined( HAVE_POSIX_MEMALIGN )
if ( ( 0 != posix_memalign( &m_allocated, 16, alloc_len ) ) if ( ( 0 != posix_memalign( &m_allocated, 16, alloc_len ) ) || ( m_allocated == NULL ) ) {
|| ( m_allocated == NULL ) ) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
m_data = (char *) m_allocated; m_data = (char*)m_allocated;
#else #else
/* malloc() a region 15 bytes larger than we need, and find /* malloc() a region 15 bytes larger than we need, and find
@@ -91,15 +88,15 @@ AlignedBuffer::AlignedBuffer( size_t len, const char *data )
throw std::bad_alloc(); throw std::bad_alloc();
} }
uintptr_t iptr = (uintptr_t) m_allocated; uintptr_t iptr = (uintptr_t)m_allocated;
if ( iptr & 0xF ) { if ( iptr & 0xF ) {
iptr += 16 - ( iptr & 0xF ); iptr += 16 - ( iptr & 0xF );
} }
assert( !( iptr & 0xF ) ); assert( !( iptr & 0xF ) );
assert( iptr >= (uintptr_t) m_allocated ); assert( iptr >= (uintptr_t)m_allocated );
assert( iptr <= ( 15 + (uintptr_t) m_allocated ) ); assert( iptr <= ( 15 + (uintptr_t)m_allocated ) );
m_data = (char *) iptr; m_data = (char*)iptr;
#endif /* !defined(HAVE_POSIX_MEMALIGN) */ #endif /* !defined(HAVE_POSIX_MEMALIGN) */
@@ -136,32 +133,28 @@ Base64Key::Base64Key()
PRNG().fill( key, sizeof( key ) ); PRNG().fill( key, sizeof( key ) );
} }
Base64Key::Base64Key(PRNG &prng) Base64Key::Base64Key( PRNG& prng )
{ {
prng.fill( key, sizeof( key ) ); prng.fill( key, sizeof( key ) );
} }
std::string Base64Key::printable_key( void ) const std::string Base64Key::printable_key( void ) const
{ {
char base64[ 24 ]; char base64[24];
base64_encode( key, 16, base64, 24 ); base64_encode( key, 16, base64, 24 );
if ( (base64[ 23 ] != '=') if ( ( base64[23] != '=' ) || ( base64[22] != '=' ) ) {
|| (base64[ 22 ] != '=') ) {
throw CryptoException( std::string( "Unexpected output from base64_encode: " ) + std::string( base64, 24 ) ); throw CryptoException( std::string( "Unexpected output from base64_encode: " ) + std::string( base64, 24 ) );
} }
base64[ 22 ] = 0; base64[22] = 0;
return std::string( base64 ); return std::string( base64 );
} }
Session::Session( Base64Key s_key ) Session::Session( Base64Key s_key )
: key( s_key ), ctx_buf( ae_ctx_sizeof() ), : key( s_key ), ctx_buf( ae_ctx_sizeof() ), ctx( (ae_ctx*)ctx_buf.data() ), blocks_encrypted( 0 ),
ctx( (ae_ctx *)ctx_buf.data() ), blocks_encrypted( 0 ), plaintext_buffer( RECEIVE_MTU ), ciphertext_buffer( RECEIVE_MTU ), nonce_buffer( Nonce::NONCE_LEN )
plaintext_buffer( RECEIVE_MTU ),
ciphertext_buffer( RECEIVE_MTU ),
nonce_buffer( Nonce::NONCE_LEN )
{ {
if ( AE_SUCCESS != ae_init( ctx, key.data(), 16, 12, 16 ) ) { if ( AE_SUCCESS != ae_init( ctx, key.data(), 16, 12, 16 ) ) {
throw CryptoException( "Could not initialize AES-OCB context." ); throw CryptoException( "Could not initialize AES-OCB context." );
@@ -188,7 +181,7 @@ uint64_t Nonce::val( void ) const
return be64toh( ret ); return be64toh( ret );
} }
Nonce::Nonce( const char *s_bytes, size_t len ) Nonce::Nonce( const char* s_bytes, size_t len )
{ {
if ( len != 8 ) { if ( len != 8 ) {
throw CryptoException( "Nonce representation must be 8 octets long." ); throw CryptoException( "Nonce representation must be 8 octets long." );
@@ -198,7 +191,7 @@ Nonce::Nonce( const char *s_bytes, size_t len )
memcpy( bytes + 4, s_bytes, 8 ); memcpy( bytes + 4, s_bytes, 8 );
} }
const std::string Session::encrypt( const Message & plaintext ) const std::string Session::encrypt( const Message& plaintext )
{ {
const size_t pt_len = plaintext.text.size(); const size_t pt_len = plaintext.text.size();
const int ciphertext_len = pt_len + 16; const int ciphertext_len = pt_len + 16;
@@ -209,7 +202,8 @@ const std::string Session::encrypt( const Message & plaintext )
memcpy( plaintext_buffer.data(), plaintext.text.data(), pt_len ); memcpy( plaintext_buffer.data(), plaintext.text.data(), pt_len );
memcpy( nonce_buffer.data(), plaintext.nonce.data(), Nonce::NONCE_LEN ); memcpy( nonce_buffer.data(), plaintext.nonce.data(), Nonce::NONCE_LEN );
if ( ciphertext_len != ae_encrypt( ctx, /* ctx */ if ( ciphertext_len
!= ae_encrypt( ctx, /* ctx */
nonce_buffer.data(), /* nonce */ nonce_buffer.data(), /* nonce */
plaintext_buffer.data(), /* pt */ plaintext_buffer.data(), /* pt */
pt_len, /* pt_len */ pt_len, /* pt_len */
@@ -248,7 +242,7 @@ const std::string Session::encrypt( const Message & plaintext )
return plaintext.nonce.cc_str() + text; return plaintext.nonce.cc_str() + text;
} }
const Message Session::decrypt( const char *str, size_t len ) const Message Session::decrypt( const char* str, size_t len )
{ {
if ( len < 24 ) { if ( len < 24 ) {
throw CryptoException( "Ciphertext must contain nonce and tag." ); throw CryptoException( "Ciphertext must contain nonce and tag." );
@@ -269,7 +263,8 @@ const Message Session::decrypt( const char *str, size_t len )
memcpy( ciphertext_buffer.data(), str + 8, body_len ); memcpy( ciphertext_buffer.data(), str + 8, body_len );
memcpy( nonce_buffer.data(), nonce.data(), Nonce::NONCE_LEN ); memcpy( nonce_buffer.data(), nonce.data(), Nonce::NONCE_LEN );
if ( pt_len != ae_decrypt( ctx, /* ctx */ if ( pt_len
!= ae_decrypt( ctx, /* ctx */
nonce_buffer.data(), /* nonce */ nonce_buffer.data(), /* nonce */
ciphertext_buffer.data(), /* ct */ ciphertext_buffer.data(), /* ct */
body_len, /* ct_len */ body_len, /* ct_len */
@@ -290,7 +285,8 @@ static rlim_t saved_core_rlimit;
/* Disable dumping core, as a precaution to avoid saving sensitive data /* Disable dumping core, as a precaution to avoid saving sensitive data
to disk. */ to disk. */
void Crypto::disable_dumping_core( void ) { void Crypto::disable_dumping_core( void )
{
struct rlimit limit; struct rlimit limit;
if ( 0 != getrlimit( RLIMIT_CORE, &limit ) ) { if ( 0 != getrlimit( RLIMIT_CORE, &limit ) ) {
/* We don't throw CryptoException because this is called very early /* We don't throw CryptoException because this is called very early
@@ -307,7 +303,8 @@ void Crypto::disable_dumping_core( void ) {
} }
} }
void Crypto::reenable_dumping_core( void ) { void Crypto::reenable_dumping_core( void )
{
/* Silent failure is safe. */ /* Silent failure is safe. */
struct rlimit limit; struct rlimit limit;
if ( 0 == getrlimit( RLIMIT_CORE, &limit ) ) { if ( 0 == getrlimit( RLIMIT_CORE, &limit ) ) {
+62 -65
View File
@@ -41,107 +41,106 @@
#include <exception> #include <exception>
#include <string> #include <string>
long int myatoi( const char* str );
long int myatoi( const char *str );
class PRNG; class PRNG;
namespace Crypto { namespace Crypto {
class CryptoException : public std::exception { class CryptoException : public std::exception
public: {
public:
std::string text; std::string text;
bool fatal; bool fatal;
CryptoException( std::string s_text, bool s_fatal = false ) CryptoException( std::string s_text, bool s_fatal = false ) : text( s_text ), fatal( s_fatal ) {};
: text( s_text ), fatal( s_fatal ) {}; const char* what() const throw() { return text.c_str(); }
const char *what() const throw () { return text.c_str(); } ~CryptoException() throw() {}
~CryptoException() throw () {} };
};
/* /*
* OCB (and other algorithms) require a source of nonce/sequence * OCB (and other algorithms) require a source of nonce/sequence
* numbers that never repeats its output. Enforce that with this * numbers that never repeats its output. Enforce that with this
* function. * function.
*/ */
uint64_t unique( void ); uint64_t unique( void );
/* 16-byte-aligned buffer, with length. */ /* 16-byte-aligned buffer, with length. */
class AlignedBuffer { class AlignedBuffer
private: {
private:
size_t m_len; size_t m_len;
void *m_allocated; void* m_allocated;
char *m_data; char* m_data;
public: public:
AlignedBuffer( size_t len, const char *data = NULL ); AlignedBuffer( size_t len, const char* data = NULL );
~AlignedBuffer() { ~AlignedBuffer() { free( m_allocated ); }
free( m_allocated );
}
char * data( void ) const { return m_data; } char* data( void ) const { return m_data; }
size_t len( void ) const { return m_len; } size_t len( void ) const { return m_len; }
private: private:
/* Not implemented */ /* Not implemented */
AlignedBuffer( const AlignedBuffer & ); AlignedBuffer( const AlignedBuffer& );
AlignedBuffer & operator=( const AlignedBuffer & ); AlignedBuffer& operator=( const AlignedBuffer& );
}; };
class Base64Key { class Base64Key
private: {
unsigned char key[ 16 ]; private:
unsigned char key[16];
public: public:
Base64Key(); /* random key */ Base64Key(); /* random key */
Base64Key(PRNG &prng); Base64Key( PRNG& prng );
Base64Key( std::string printable_key ); Base64Key( std::string printable_key );
std::string printable_key( void ) const; std::string printable_key( void ) const;
unsigned char *data( void ) { return key; } unsigned char* data( void ) { return key; }
}; };
class Nonce { class Nonce
public: {
public:
static const int NONCE_LEN = 12; static const int NONCE_LEN = 12;
private: private:
char bytes[ NONCE_LEN ]; char bytes[NONCE_LEN];
public: public:
Nonce( uint64_t val ); Nonce( uint64_t val );
Nonce( const char *s_bytes, size_t len ); Nonce( const char* s_bytes, size_t len );
std::string cc_str( void ) const { return std::string( bytes + 4, 8 ); } std::string cc_str( void ) const { return std::string( bytes + 4, 8 ); }
const char *data( void ) const { return bytes; } const char* data( void ) const { return bytes; }
uint64_t val( void ) const; uint64_t val( void ) const;
}; };
class Message { class Message
public: {
public:
const Nonce nonce; const Nonce nonce;
const std::string text; const std::string text;
Message( const char *nonce_bytes, size_t nonce_len, Message( const char* nonce_bytes, size_t nonce_len, const char* text_bytes, size_t text_len )
const char *text_bytes, size_t text_len ) : nonce( nonce_bytes, nonce_len ), text( text_bytes, text_len )
: nonce( nonce_bytes, nonce_len ), {}
text( text_bytes, text_len ) {}
Message( const Nonce & s_nonce, const std::string & s_text ) Message( const Nonce& s_nonce, const std::string& s_text ) : nonce( s_nonce ), text( s_text ) {}
: nonce( s_nonce ), };
text( s_text ) {}
};
class Session { class Session
private: {
private:
Base64Key key; Base64Key key;
AlignedBuffer ctx_buf; AlignedBuffer ctx_buf;
ae_ctx *ctx; ae_ctx* ctx;
uint64_t blocks_encrypted; uint64_t blocks_encrypted;
AlignedBuffer plaintext_buffer; AlignedBuffer plaintext_buffer;
AlignedBuffer ciphertext_buffer; AlignedBuffer ciphertext_buffer;
AlignedBuffer nonce_buffer; AlignedBuffer nonce_buffer;
public: public:
static const int RECEIVE_MTU = 2048; static const int RECEIVE_MTU = 2048;
/* Overhead (not counting the nonce, which is handled by network transport) */ /* Overhead (not counting the nonce, which is handled by network transport) */
static const int ADDED_BYTES = 16 /* final OCB block */; static const int ADDED_BYTES = 16 /* final OCB block */;
@@ -149,18 +148,16 @@ namespace Crypto {
Session( Base64Key s_key ); Session( Base64Key s_key );
~Session(); ~Session();
const std::string encrypt( const Message & plaintext ); const std::string encrypt( const Message& plaintext );
const Message decrypt( const char *str, size_t len ); const Message decrypt( const char* str, size_t len );
const Message decrypt( const std::string & ciphertext ) { const Message decrypt( const std::string& ciphertext ) { return decrypt( ciphertext.data(), ciphertext.size() ); }
return decrypt( ciphertext.data(), ciphertext.size() );
}
Session( const Session & ); Session( const Session& );
Session & operator=( const Session & ); Session& operator=( const Session& );
}; };
void disable_dumping_core( void ); void disable_dumping_core( void );
void reenable_dumping_core( void ); void reenable_dumping_core( void );
} }
#endif #endif
+97 -76
View File
@@ -1,188 +1,209 @@
#include "src/include/config.h"
#include "src/crypto/ae.h" #include "src/crypto/ae.h"
#include "src/include/config.h"
#include <cstring> #include <cstring>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/evp.h> #include <openssl/evp.h>
struct _ae_ctx { struct _ae_ctx
EVP_CIPHER_CTX *enc_ctx; {
EVP_CIPHER_CTX *dec_ctx; EVP_CIPHER_CTX* enc_ctx;
EVP_CIPHER_CTX* dec_ctx;
int tag_len; int tag_len;
}; };
int ae_clear(ae_ctx* ctx) { int ae_clear( ae_ctx* ctx )
EVP_CIPHER_CTX_free(ctx->enc_ctx); {
EVP_CIPHER_CTX_free(ctx->dec_ctx); EVP_CIPHER_CTX_free( ctx->enc_ctx );
OPENSSL_cleanse(ctx, sizeof(*ctx)); EVP_CIPHER_CTX_free( ctx->dec_ctx );
OPENSSL_cleanse( ctx, sizeof( *ctx ) );
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_ctx_sizeof() { int ae_ctx_sizeof()
return sizeof(_ae_ctx); {
return sizeof( _ae_ctx );
} }
// If direction is 1, initializes encryption. If 0, initializes // If direction is 1, initializes encryption. If 0, initializes
// decryption. See the documentation of EVP_CipherInit_ex // decryption. See the documentation of EVP_CipherInit_ex
static int ae_evp_cipher_init(EVP_CIPHER_CTX **in_ctx, int direction, static int ae_evp_cipher_init( EVP_CIPHER_CTX** in_ctx,
const unsigned char *key, int direction,
int nonce_len, int tag_len) { const unsigned char* key,
int nonce_len,
int tag_len )
{
// Create an OpenSSL EVP context. It does not yet have any specific // Create an OpenSSL EVP context. It does not yet have any specific
// cipher associated with it. // cipher associated with it.
if (!(*in_ctx = EVP_CIPHER_CTX_new())) { if ( !( *in_ctx = EVP_CIPHER_CTX_new() ) ) {
return -3; return -3;
} }
EVP_CIPHER_CTX *ctx = *in_ctx; EVP_CIPHER_CTX* ctx = *in_ctx;
// Although OCB-AES has the same initialization process between // Although OCB-AES has the same initialization process between
// encryption and decryption, an EVP_CIPHER_CTX must be initialized // encryption and decryption, an EVP_CIPHER_CTX must be initialized
// for a specific direction. // for a specific direction.
if (EVP_CipherInit_ex(ctx, EVP_aes_128_ocb(), if ( EVP_CipherInit_ex( ctx,
/*impl=*/NULL, /*key=*/key, /*iv=*/NULL, EVP_aes_128_ocb(),
direction) != 1) { /*impl=*/NULL,
/*key=*/key,
/*iv=*/NULL,
direction )
!= 1 ) {
return -3; return -3;
} }
// Attempt to set the nonce length. If it fails, the length must not // Attempt to set the nonce length. If it fails, the length must not
// be supported. However, that should have been handled by the // be supported. However, that should have been handled by the
// pre-condition check above. // pre-condition check above.
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, if ( EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, NULL ) != 1 ) {
nonce_len, NULL) != 1) {
return -3; return -3;
} }
// A NULL tag length means that EVP_CTRL_AEAD_SET_TAG is only being // A NULL tag length means that EVP_CTRL_AEAD_SET_TAG is only being
// used to set the length // used to set the length
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL ) != 1 ) {
tag_len, NULL) != 1) {
return -3; return -3;
} }
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_init(ae_ctx *ctx, const void *key, int key_len, int nonce_len, int ae_init( ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len )
int tag_len) { {
// Pre-condition: Only nonces of length 12 are supported. The // Pre-condition: Only nonces of length 12 are supported. The
// documentation of `ae_init` in ae.h specifies that `ctx` is // documentation of `ae_init` in ae.h specifies that `ctx` is
// untouched if an invalid configuration is requested. Delegating // untouched if an invalid configuration is requested. Delegating
// this to OpenSSL would happen too late; `ctx` has already been // this to OpenSSL would happen too late; `ctx` has already been
// modified. // modified.
if (nonce_len != 12) { if ( nonce_len != 12 ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
// Pre-condition: Only AES-128 is supported. // Pre-condition: Only AES-128 is supported.
if (key_len != 16) { if ( key_len != 16 ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
int r = AE_SUCCESS; int r = AE_SUCCESS;
if ((r = ae_evp_cipher_init(&ctx->enc_ctx, 1, if ( ( r = ae_evp_cipher_init(
reinterpret_cast<const unsigned char *>(key), &ctx->enc_ctx, 1, reinterpret_cast<const unsigned char*>( key ), nonce_len, tag_len ) )
nonce_len, tag_len))!= AE_SUCCESS) { != AE_SUCCESS ) {
return r; return r;
} }
if ((r = ae_evp_cipher_init(&ctx->dec_ctx, 0, if ( ( r = ae_evp_cipher_init(
reinterpret_cast<const unsigned char *>(key), &ctx->dec_ctx, 0, reinterpret_cast<const unsigned char*>( key ), nonce_len, tag_len ) )
nonce_len, tag_len)) != AE_SUCCESS) { != AE_SUCCESS ) {
return r; return r;
} }
ctx->tag_len = tag_len; ctx->tag_len = tag_len;
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_encrypt(ae_ctx *ctx, const void *nonce_ptr, const void *pt_ptr, int ae_encrypt( ae_ctx* ctx,
int pt_len, const void *ad_ptr, int ad_len, void *ct_ptr, const void* nonce_ptr,
void *tag, int final) { const void* pt_ptr,
const unsigned char *nonce = int pt_len,
reinterpret_cast<const unsigned char *>(nonce_ptr); const void* ad_ptr,
const unsigned char *pt = reinterpret_cast<const unsigned char *>(pt_ptr); int ad_len,
const unsigned char* ad = reinterpret_cast<const unsigned char *>(ad_ptr); void* ct_ptr,
unsigned char* ct = reinterpret_cast<unsigned char *>(ct_ptr); void* tag,
int final )
{
const unsigned char* nonce = reinterpret_cast<const unsigned char*>( nonce_ptr );
const unsigned char* pt = reinterpret_cast<const unsigned char*>( pt_ptr );
const unsigned char* ad = reinterpret_cast<const unsigned char*>( ad_ptr );
unsigned char* ct = reinterpret_cast<unsigned char*>( ct_ptr );
// Streaming mode is not supported; nonce must always be provided. // Streaming mode is not supported; nonce must always be provided.
if (final != AE_FINALIZE) { if ( final != AE_FINALIZE ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (nonce == NULL) { if ( nonce == NULL ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (EVP_EncryptInit_ex(ctx->enc_ctx, /*type=*/NULL, /*impl=*/NULL, if ( EVP_EncryptInit_ex( ctx->enc_ctx,
/*key=*/NULL, nonce) != 1) { /*type=*/NULL,
/*impl=*/NULL,
/*key=*/NULL,
nonce )
!= 1 ) {
return -3; return -3;
} }
int len = 0; int len = 0;
if (ad != NULL && ad_len > 0 && if ( ad != NULL && ad_len > 0 && EVP_EncryptUpdate( ctx->enc_ctx, /*out=*/NULL, &len, ad, ad_len ) != 1 ) {
EVP_EncryptUpdate(ctx->enc_ctx, /*out=*/NULL, &len, ad, ad_len) != 1) {
return -3; return -3;
} }
len = 0; len = 0;
if (pt != NULL && pt_len > 0 && if ( pt != NULL && pt_len > 0 && EVP_EncryptUpdate( ctx->enc_ctx, ct, &len, pt, pt_len ) != 1 ) {
EVP_EncryptUpdate(ctx->enc_ctx, ct, &len, pt, pt_len) != 1) {
return -3; return -3;
} }
int ciphertext_len = len; int ciphertext_len = len;
if (EVP_EncryptFinal_ex(ctx->enc_ctx, ct + ciphertext_len, &len) != 1) { if ( EVP_EncryptFinal_ex( ctx->enc_ctx, ct + ciphertext_len, &len ) != 1 ) {
return -3; return -3;
} }
ciphertext_len += len; ciphertext_len += len;
// If `tag` is provided, the authentication tag goes // If `tag` is provided, the authentication tag goes
// there. Otherwise, it is appended after the ciphertext. // there. Otherwise, it is appended after the ciphertext.
void *tag_location = tag != NULL ? tag : ct + ciphertext_len; void* tag_location = tag != NULL ? tag : ct + ciphertext_len;
if (EVP_CIPHER_CTX_ctrl(ctx->enc_ctx, EVP_CTRL_AEAD_GET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx->enc_ctx, EVP_CTRL_AEAD_GET_TAG, ctx->tag_len, tag_location ) != 1 ) {
ctx->tag_len, tag_location) != 1) {
return -3; return -3;
} }
if (tag == NULL) { if ( tag == NULL ) {
ciphertext_len += ctx->tag_len; ciphertext_len += ctx->tag_len;
} }
return ciphertext_len; return ciphertext_len;
} }
int ae_decrypt( ae_ctx* ctx,
int ae_decrypt(ae_ctx *ctx, const void *nonce_ptr, const void *ct_ptr, const void* nonce_ptr,
int ct_len, const void *ad_ptr, int ad_len, void *pt_ptr, const void* ct_ptr,
const void *tag, int final) { int ct_len,
const unsigned char *nonce = const void* ad_ptr,
reinterpret_cast<const unsigned char *>(nonce_ptr); int ad_len,
const unsigned char *ct = reinterpret_cast<const unsigned char *>(ct_ptr); void* pt_ptr,
const unsigned char* ad = reinterpret_cast<const unsigned char *>(ad_ptr); const void* tag,
unsigned char* pt = reinterpret_cast<unsigned char *>(pt_ptr); int final )
if (ct_len < ctx->tag_len) { {
const unsigned char* nonce = reinterpret_cast<const unsigned char*>( nonce_ptr );
const unsigned char* ct = reinterpret_cast<const unsigned char*>( ct_ptr );
const unsigned char* ad = reinterpret_cast<const unsigned char*>( ad_ptr );
unsigned char* pt = reinterpret_cast<unsigned char*>( pt_ptr );
if ( ct_len < ctx->tag_len ) {
return AE_INVALID; return AE_INVALID;
} }
// If an external tag is not provided, then the tag is assumed to be // If an external tag is not provided, then the tag is assumed to be
// the final bytes of the cipher text. Subtract it off now so the // the final bytes of the cipher text. Subtract it off now so the
// plaintext does not accidentally try to decrypt the AEAD tag (and // plaintext does not accidentally try to decrypt the AEAD tag (and
// then cause an authentication failure). // then cause an authentication failure).
if (tag == NULL) { if ( tag == NULL ) {
ct_len -= ctx->tag_len; ct_len -= ctx->tag_len;
} }
// Like encryption, nonce must always be provided and streaming is not supported. // Like encryption, nonce must always be provided and streaming is not supported.
if (final != AE_FINALIZE) { if ( final != AE_FINALIZE ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (nonce == NULL) { if ( nonce == NULL ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (EVP_DecryptInit_ex(ctx->dec_ctx, /*type=*/NULL, /*impl=*/NULL, if ( EVP_DecryptInit_ex( ctx->dec_ctx,
/*key=*/NULL, nonce) != 1) { /*type=*/NULL,
/*impl=*/NULL,
/*key=*/NULL,
nonce )
!= 1 ) {
return -3; return -3;
} }
int len = 0; int len = 0;
if (ad != NULL && ad_len > 0 && if ( ad != NULL && ad_len > 0 && EVP_DecryptUpdate( ctx->dec_ctx, /*out=*/NULL, &len, ad, ad_len ) != 1 ) {
EVP_DecryptUpdate(ctx->dec_ctx, /*out=*/NULL, &len, ad, ad_len) != 1) {
return -3; return -3;
} }
len = 0; len = 0;
if (ct != NULL && ct_len > 0 && if ( ct != NULL && ct_len > 0 && EVP_DecryptUpdate( ctx->dec_ctx, pt, &len, ct, ct_len ) != 1 ) {
EVP_DecryptUpdate(ctx->dec_ctx, pt, &len, ct, ct_len) != 1) {
return -3; return -3;
} }
int plaintext_len = len; int plaintext_len = len;
// If `tag` is provided, the authentication is read from // If `tag` is provided, the authentication is read from
// there. Otherwise, it's the last bytes of the ciphertext. (This is // there. Otherwise, it's the last bytes of the ciphertext. (This is
// a convention, not a requirement of OCB mode). // a convention, not a requirement of OCB mode).
const void *tag_location = tag != NULL ? tag : ct + ct_len; const void* tag_location = tag != NULL ? tag : ct + ct_len;
if (EVP_CIPHER_CTX_ctrl(ctx->dec_ctx, EVP_CTRL_AEAD_SET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx->dec_ctx, EVP_CTRL_AEAD_SET_TAG, ctx->tag_len, (void*)tag_location ) != 1 ) {
ctx->tag_len, (void *)tag_location) != 1) {
return -3; return -3;
} }
if (EVP_DecryptFinal_ex(ctx->dec_ctx, pt + plaintext_len, &len) != 1) { if ( EVP_DecryptFinal_ex( ctx->dec_ctx, pt + plaintext_len, &len ) != 1 ) {
return AE_INVALID; return AE_INVALID;
} }
plaintext_len += len; plaintext_len += len;
+15 -10
View File
@@ -47,41 +47,46 @@ static const char rdev[] = "/dev/urandom";
using namespace Crypto; using namespace Crypto;
class PRNG { class PRNG
private: {
private:
std::ifstream randfile; std::ifstream randfile;
/* unimplemented to satisfy -Weffc++ */ /* unimplemented to satisfy -Weffc++ */
PRNG( const PRNG & ); PRNG( const PRNG& );
PRNG & operator=( const PRNG & ); PRNG& operator=( const PRNG& );
public: public:
PRNG() : randfile( rdev, std::ifstream::in | std::ifstream::binary ) {} PRNG() : randfile( rdev, std::ifstream::in | std::ifstream::binary ) {}
void fill( void *dest, size_t size ) { void fill( void* dest, size_t size )
{
if ( 0 == size ) { if ( 0 == size ) {
return; return;
} }
randfile.read( static_cast<char *>( dest ), size ); randfile.read( static_cast<char*>( dest ), size );
if ( !randfile ) { if ( !randfile ) {
throw CryptoException( "Could not read from " + std::string( rdev ) ); throw CryptoException( "Could not read from " + std::string( rdev ) );
} }
} }
uint8_t uint8() { uint8_t uint8()
{
uint8_t x; uint8_t x;
fill( &x, 1 ); fill( &x, 1 );
return x; return x;
} }
uint32_t uint32() { uint32_t uint32()
{
uint32_t x; uint32_t x;
fill( &x, 4 ); fill( &x, 4 );
return x; return x;
} }
uint64_t uint64() { uint64_t uint64()
{
uint64_t x; uint64_t x;
fill( &x, 8 ); fill( &x, 8 );
return x; return x;
+22 -24
View File
@@ -53,41 +53,41 @@
#include <util.h> #include <util.h>
#endif #endif
#include "src/util/swrite.h" #include "src/frontend/terminaloverlay.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/frontend/terminaloverlay.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "src/util/swrite.h"
const int ITERATIONS = 100000; const int ITERATIONS = 100000;
using namespace Terminal; using namespace Terminal;
int main( int argc, char **argv ) int main( int argc, char** argv )
{ {
try { try {
int fbmod = 0; int fbmod = 0;
int width = 80, height = 24; int width = 80, height = 24;
int iterations = ITERATIONS; int iterations = ITERATIONS;
if (argc > 1) { if ( argc > 1 ) {
iterations = atoi(argv[1]); iterations = atoi( argv[1] );
if (iterations < 1 || iterations > 1000000000) { if ( iterations < 1 || iterations > 1000000000 ) {
fprintf(stderr, "bogus iteration count\n"); fprintf( stderr, "bogus iteration count\n" );
exit(1); exit( 1 );
} }
} }
if (argc > 3) { if ( argc > 3 ) {
width = atoi(argv[2]); width = atoi( argv[2] );
height = atoi(argv[3]); height = atoi( argv[3] );
if (width < 1 || width > 1000 || height < 1 || height > 1000) { if ( width < 1 || width > 1000 || height < 1 || height > 1000 ) {
fprintf(stderr, "bogus window size\n"); fprintf( stderr, "bogus window size\n" );
exit(1); exit( 1 );
} }
} }
Framebuffer local_framebuffers[ 2 ] = { Framebuffer(width,height), Framebuffer(width,height) }; Framebuffer local_framebuffers[2] = { Framebuffer( width, height ), Framebuffer( width, height ) };
Framebuffer *local_framebuffer = &(local_framebuffers[ fbmod ]); Framebuffer* local_framebuffer = &( local_framebuffers[fbmod] );
Framebuffer *new_state = &(local_framebuffers[ !fbmod ]); Framebuffer* new_state = &( local_framebuffers[!fbmod] );
Overlay::OverlayManager overlays; Overlay::OverlayManager overlays;
Display display( true ); Display display( true );
Complete local_terminal( width, height ); Complete local_terminal( width, height );
@@ -107,9 +107,7 @@ int main( int argc, char **argv )
overlays.apply( *new_state ); overlays.apply( *new_state );
/* calculate minimal difference from where we are */ /* calculate minimal difference from where we are */
const std::string diff( display.new_frame( false, const std::string diff( display.new_frame( false, *local_framebuffer, *new_state ) );
*local_framebuffer,
*new_state ) );
/* make sure to use diff */ /* make sure to use diff */
if ( diff.size() > INT_MAX ) { if ( diff.size() > INT_MAX ) {
@@ -117,10 +115,10 @@ int main( int argc, char **argv )
} }
fbmod = !fbmod; fbmod = !fbmod;
local_framebuffer = &(local_framebuffers[ fbmod ]); local_framebuffer = &( local_framebuffers[fbmod] );
new_state = &(local_framebuffers[ !fbmod ]); new_state = &( local_framebuffers[!fbmod] );
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Exception caught: %s\n", e.what() ); fprintf( stderr, "Exception caught: %s\n", e.what() );
return 1; return 1;
} }
+5 -6
View File
@@ -38,15 +38,15 @@
using namespace Crypto; using namespace Crypto;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc != 2 ) { if ( argc != 2 ) {
fprintf( stderr, "Usage: %s KEY\n", argv[ 0 ] ); fprintf( stderr, "Usage: %s KEY\n", argv[0] );
return 1; return 1;
} }
try { try {
Base64Key key( argv[ 1 ] ); Base64Key key( argv[1] );
Session session( key ); Session session( key );
/* Read input */ /* Read input */
@@ -57,10 +57,9 @@ int main( int argc, char *argv[] )
Message message = session.decrypt( input.str() ); Message message = session.decrypt( input.str() );
fprintf( stderr, "Nonce = %ld\n", fprintf( stderr, "Nonce = %ld\n", (long)message.nonce.val() );
(long)message.nonce.val() );
std::cout << message.text; std::cout << message.text;
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
exit( 1 ); exit( 1 );
} }
+4 -4
View File
@@ -38,17 +38,17 @@
using namespace Crypto; using namespace Crypto;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc != 2 ) { if ( argc != 2 ) {
fprintf( stderr, "Usage: %s NONCE\n", argv[ 0 ] ); fprintf( stderr, "Usage: %s NONCE\n", argv[0] );
return 1; return 1;
} }
try { try {
Base64Key key; Base64Key key;
Session session( key ); Session session( key );
Nonce nonce( myatoi( argv[ 1 ] ) ); Nonce nonce( myatoi( argv[1] ) );
/* Read input */ /* Read input */
std::ostringstream input; std::ostringstream input;
@@ -61,7 +61,7 @@ int main( int argc, char *argv[] )
std::cerr << "Key: " << key.printable_key() << std::endl; std::cerr << "Key: " << key.printable_key() << std::endl;
std::cout << ciphertext; std::cout << ciphertext;
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
exit( 1 ); exit( 1 );
} }
+21 -24
View File
@@ -35,38 +35,38 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include "src/network/networktransport-impl.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/network/networktransport-impl.h"
#include "src/util/select.h" #include "src/util/select.h"
using namespace Network; using namespace Network;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
bool server = true; bool server = true;
char *key; char* key;
char *ip; char* ip;
char *port; char* port;
UserStream me, remote; UserStream me, remote;
using NetworkPointer = std::shared_ptr<Transport<UserStream, UserStream>>; using NetworkPointer = std::shared_ptr<Transport<UserStream, UserStream>>;
Transport<UserStream, UserStream> *raw_n; Transport<UserStream, UserStream>* raw_n;
try { try {
if ( argc > 1 ) { if ( argc > 1 ) {
server = false; server = false;
/* client */ /* client */
key = argv[ 1 ]; key = argv[1];
ip = argv[ 2 ]; ip = argv[2];
port = argv[ 3 ]; port = argv[3];
raw_n = new Transport<UserStream, UserStream>( me, remote, key, ip, port ); raw_n = new Transport<UserStream, UserStream>( me, remote, key, ip, port );
} else { } else {
raw_n = new Transport<UserStream, UserStream>( me, remote, NULL, NULL ); raw_n = new Transport<UserStream, UserStream>( me, remote, NULL, NULL );
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Fatal startup error: %s\n", e.what() ); fprintf( stderr, "Fatal startup error: %s\n", e.what() );
exit( 1 ); exit( 1 );
} }
@@ -74,12 +74,12 @@ int main( int argc, char *argv[] )
fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() ); fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() );
if ( server ) { if ( server ) {
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
uint64_t last_num = n->get_remote_state_num(); uint64_t last_num = n->get_remote_state_num();
while ( true ) { while ( true ) {
try { try {
sel.clear_fds(); sel.clear_fds();
std::vector< int > fd_list( n->fds() ); std::vector<int> fd_list( n->fds() );
assert( fd_list.size() == 1 ); /* servers don't hop */ assert( fd_list.size() == 1 ); /* servers don't hop */
int network_fd = fd_list.back(); int network_fd = fd_list.back();
sel.add_fd( network_fd ); sel.add_fd( network_fd );
@@ -94,11 +94,12 @@ int main( int argc, char *argv[] )
n->recv(); n->recv();
if ( n->get_remote_state_num() != last_num ) { if ( n->get_remote_state_num() != last_num ) {
fprintf( stderr, "[%d=>%d %s]", (int)last_num, (int)n->get_remote_state_num(), n->get_remote_diff().c_str() ); fprintf(
stderr, "[%d=>%d %s]", (int)last_num, (int)n->get_remote_state_num(), n->get_remote_diff().c_str() );
last_num = n->get_remote_state_num(); last_num = n->get_remote_state_num();
} }
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Server error: %s\n", e.what() ); fprintf( stderr, "Server error: %s\n", e.what() );
} }
} }
@@ -120,16 +121,14 @@ int main( int argc, char *argv[] )
exit( 1 ); exit( 1 );
} }
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
while( true ) { while ( true ) {
sel.clear_fds(); sel.clear_fds();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
std::vector< int > fd_list( n->fds() ); std::vector<int> fd_list( n->fds() );
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end();
it++ ) {
sel.add_fd( *it ); sel.add_fd( *it );
} }
@@ -147,9 +146,7 @@ int main( int argc, char *argv[] )
} }
bool network_ready_to_read = false; bool network_ready_to_read = false;
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end();
it++ ) {
if ( sel.read( *it ) ) { if ( sel.read( *it ) ) {
/* packet received from the network */ /* packet received from the network */
/* we only read one socket each run */ /* we only read one socket each run */
@@ -160,7 +157,7 @@ int main( int argc, char *argv[] )
if ( network_ready_to_read ) { if ( network_ready_to_read ) {
n->recv(); n->recv();
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Client error: %s\n", e.what() ); fprintf( stderr, "Client error: %s\n", e.what() );
} }
} }
+19 -21
View File
@@ -55,21 +55,19 @@
#endif #endif
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
#include "src/util/swrite.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
const size_t buf_size = 1024; const size_t buf_size = 1024;
static void emulate_terminal( int fd ); static void emulate_terminal( int fd );
static int copy( int src, int dest ); static int copy( int src, int dest );
static int vt_parser( int fd, Parser::UTF8Parser *parser ); static int vt_parser( int fd, Parser::UTF8Parser* parser );
int main( int argc __attribute__((unused)), int main( int argc __attribute__( ( unused ) ), char* argv[] __attribute__( ( unused ) ), char* envp[] )
char *argv[] __attribute__((unused)),
char *envp[] )
{ {
int master; int master;
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
@@ -85,12 +83,14 @@ int main( int argc __attribute__((unused)),
child_termios = saved_termios; child_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(child_termios.c_iflag & IUTF8) ) { if ( !( child_termios.c_iflag & IUTF8 ) ) {
fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
child_termios.c_iflag |= IUTF8; child_termios.c_iflag |= IUTF8;
} }
#else #else
fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); fprintf( stderr,
"Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does "
"not work properly on this platform.\n" );
#endif /* HAVE_IUTF8 */ #endif /* HAVE_IUTF8 */
pid_t child = forkpty( &master, NULL, &child_termios, NULL ); pid_t child = forkpty( &master, NULL, &child_termios, NULL );
@@ -102,11 +102,11 @@ int main( int argc __attribute__((unused)),
if ( child == 0 ) { if ( child == 0 ) {
/* child */ /* child */
char *my_argv[ 2 ]; char* my_argv[2];
my_argv[ 0 ] = strdup( "/bin/bash" ); my_argv[0] = strdup( "/bin/bash" );
assert( my_argv[ 0 ] ); assert( my_argv[0] );
my_argv[ 1 ] = NULL; my_argv[1] = NULL;
if ( execve( "/bin/bash", my_argv, envp ) < 0 ) { if ( execve( "/bin/bash", my_argv, envp ) < 0 ) {
perror( "execve" ); perror( "execve" );
@@ -139,7 +139,7 @@ static void emulate_terminal( int fd )
{ {
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
sel.add_fd( fd ); sel.add_fd( fd );
@@ -166,7 +166,7 @@ static void emulate_terminal( int fd )
static int copy( int src, int dest ) static int copy( int src, int dest )
{ {
char buf[ buf_size ]; char buf[buf_size];
ssize_t bytes_read = read( src, buf, buf_size ); ssize_t bytes_read = read( src, buf, buf_size );
if ( bytes_read == 0 ) { /* EOF */ if ( bytes_read == 0 ) { /* EOF */
@@ -179,9 +179,9 @@ static int copy( int src, int dest )
return swrite( dest, buf, bytes_read ); return swrite( dest, buf, bytes_read );
} }
static int vt_parser( int fd, Parser::UTF8Parser *parser ) static int vt_parser( int fd, Parser::UTF8Parser* parser )
{ {
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
@@ -195,13 +195,11 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser )
/* feed to parser */ /* feed to parser */
Parser::Actions actions; Parser::Actions actions;
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
parser->input( buf[ i ], actions ); parser->input( buf[i], actions );
for ( Parser::Actions::iterator j = actions.begin(); for ( Parser::Actions::iterator j = actions.begin(); j != actions.end(); j++ ) {
j != actions.end();
j++ ) {
assert( *j ); assert( *j );
Parser::Action &act = **j; Parser::Action& act = **j;
if ( act.char_present ) { if ( act.char_present ) {
if ( iswprint( act.ch ) ) { if ( iswprint( act.ch ) ) {
+24 -25
View File
@@ -61,19 +61,19 @@
#include <libutil.h> #include <libutil.h>
#endif #endif
#include "src/terminal/parser.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/util/swrite.h" #include "src/terminal/parser.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/pty_compat.h"
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
#include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
const size_t buf_size = 16384; const size_t buf_size = 16384;
static void emulate_terminal( int fd ); static void emulate_terminal( int fd );
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
int master; int master;
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
@@ -89,12 +89,14 @@ int main( int argc, char *argv[] )
child_termios = saved_termios; child_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(child_termios.c_iflag & IUTF8) ) { if ( !( child_termios.c_iflag & IUTF8 ) ) {
fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
child_termios.c_iflag |= IUTF8; child_termios.c_iflag |= IUTF8;
} }
#else #else
fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); fprintf( stderr,
"Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does "
"not work properly on this platform.\n" );
#endif /* HAVE_IUTF8 */ #endif /* HAVE_IUTF8 */
pid_t child = forkpty( &master, NULL, &child_termios, NULL ); pid_t child = forkpty( &master, NULL, &child_termios, NULL );
@@ -117,26 +119,26 @@ int main( int argc, char *argv[] )
exit( 1 ); exit( 1 );
} }
char *my_argv[ 2 ]; char* my_argv[2];
if ( argc > 1 ) { if ( argc > 1 ) {
argv++; argv++;
} else { } else {
/* get shell name */ /* get shell name */
my_argv[ 0 ] = getenv( "SHELL" ); my_argv[0] = getenv( "SHELL" );
if ( my_argv[ 0 ] == NULL || *my_argv[ 0 ] == '\0' ) { if ( my_argv[0] == NULL || *my_argv[0] == '\0' ) {
struct passwd *pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if ( pw == NULL ) { if ( pw == NULL ) {
perror( "getpwuid" ); perror( "getpwuid" );
exit( 1 ); exit( 1 );
} }
my_argv[ 0 ] = strdup( pw->pw_shell ); my_argv[0] = strdup( pw->pw_shell );
} }
assert( my_argv[ 0 ] ); assert( my_argv[0] );
my_argv[ 1 ] = NULL; my_argv[1] = NULL;
argv = my_argv; argv = my_argv;
} }
if ( execvp( argv[ 0 ], argv ) < 0 ) { if ( execvp( argv[0], argv ) < 0 ) {
perror( "execve" ); perror( "execve" );
exit( 1 ); exit( 1 );
} }
@@ -154,7 +156,7 @@ int main( int argc, char *argv[] )
try { try {
emulate_terminal( master ); emulate_terminal( master );
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "\r\nException caught: %s\r\n", e.what() ); fprintf( stderr, "\r\nException caught: %s\r\n", e.what() );
} }
@@ -170,8 +172,7 @@ int main( int argc, char *argv[] )
} }
/* Print a frame if the last frame was more than 1/50 seconds ago */ /* Print a frame if the last frame was more than 1/50 seconds ago */
static bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame, static bool tick( Terminal::Framebuffer& state, Terminal::Framebuffer& new_frame, const Terminal::Display& display )
const Terminal::Display &display )
{ {
static bool initialized = false; static bool initialized = false;
static struct timeval last_time; static struct timeval last_time;
@@ -182,11 +183,9 @@ static bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame
perror( "gettimeofday" ); perror( "gettimeofday" );
} }
double diff = (this_time.tv_sec - last_time.tv_sec) double diff = ( this_time.tv_sec - last_time.tv_sec ) + .000001 * ( this_time.tv_usec - last_time.tv_usec );
+ .000001 * (this_time.tv_usec - last_time.tv_usec);
if ( (!initialized) if ( ( !initialized ) || ( diff >= 0.02 ) ) {
|| (diff >= 0.02) ) {
std::string update = display.new_frame( initialized, state, new_frame ); std::string update = display.new_frame( initialized, state, new_frame );
swrite( STDOUT_FILENO, update.c_str() ); swrite( STDOUT_FILENO, update.c_str() );
state = new_frame; state = new_frame;
@@ -239,7 +238,7 @@ static void emulate_terminal( int fd )
/* open display */ /* open display */
Terminal::Display display( true ); /* use TERM to initialize */ Terminal::Display display( true ); /* use TERM to initialize */
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
sel.add_fd( fd ); sel.add_fd( fd );
sel.add_signal( SIGWINCH ); sel.add_signal( SIGWINCH );
@@ -257,7 +256,7 @@ static void emulate_terminal( int fd )
if ( sel.read( STDIN_FILENO ) ) { if ( sel.read( STDIN_FILENO ) ) {
/* input from user */ /* input from user */
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size ); ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size );
@@ -271,7 +270,7 @@ static void emulate_terminal( int fd )
std::string terminal_to_host; std::string terminal_to_host;
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
terminal_to_host += complete.act( Parser::UserByte( buf[ i ] ) ); terminal_to_host += complete.act( Parser::UserByte( buf[i] ) );
} }
if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) {
@@ -279,7 +278,7 @@ static void emulate_terminal( int fd )
} }
} else if ( sel.read( fd ) ) { } else if ( sel.read( fd ) ) {
/* input from host */ /* input from host */
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
+45 -45
View File
@@ -37,10 +37,10 @@
#include <unistd.h> #include <unistd.h>
#include "stmclient.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "stmclient.h"
/* These need to be included last because of conflicting defines. */ /* These need to be included last because of conflicting defines. */
/* /*
@@ -53,59 +53,62 @@
#endif #endif
#if defined HAVE_NCURSESW_CURSES_H #if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h> #include <ncursesw/curses.h>
# include <ncursesw/term.h> #include <ncursesw/term.h>
#elif defined HAVE_NCURSESW_H #elif defined HAVE_NCURSESW_H
# include <ncursesw.h> #include <ncursesw.h>
# include <term.h> #include <term.h>
#elif defined HAVE_NCURSES_CURSES_H #elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h> #include <ncurses/curses.h>
# include <ncurses/term.h> #include <ncurses/term.h>
#elif defined HAVE_NCURSES_H #elif defined HAVE_NCURSES_H
# include <ncurses.h> #include <ncurses.h>
# include <term.h> #include <term.h>
#elif defined HAVE_CURSES_H #elif defined HAVE_CURSES_H
# include <curses.h> #include <curses.h>
# include <term.h> #include <term.h>
#else #else
# error "SysV or X/Open-compatible Curses header file required" #error "SysV or X/Open-compatible Curses header file required"
#endif #endif
static void print_version( FILE *file ) static void print_version( FILE* file )
{ {
fputs( "mosh-client (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n" fputs( "mosh-client (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n"
"Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n" "This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n", file ); "There is NO WARRANTY, to the extent permitted by law.\n",
file );
} }
static void print_usage( FILE *file, const char *argv0 ) static void print_usage( FILE* file, const char* argv0 )
{ {
print_version( file ); print_version( file );
fprintf( file, "\nUsage: %s [-# 'ARGS'] IP PORT\n" fprintf( file,
" %s -c\n", argv0, argv0 ); "\nUsage: %s [-# 'ARGS'] IP PORT\n"
" %s -c\n",
argv0,
argv0 );
} }
static void print_colorcount( void ) static void print_colorcount( void )
{ {
/* check colors */ /* check colors */
setupterm((char *)0, 1, (int *)0); setupterm( (char*)0, 1, (int*)0 );
char colors_name[] = "colors"; char colors_name[] = "colors";
int color_val = tigetnum( colors_name ); int color_val = tigetnum( colors_name );
if ( color_val == -2 ) { if ( color_val == -2 ) {
fprintf( stderr, "Invalid terminfo numeric capability: %s\n", fprintf( stderr, "Invalid terminfo numeric capability: %s\n", colors_name );
colors_name );
} }
printf( "%d\n", color_val ); printf( "%d\n", color_val );
} }
#ifdef NACL #ifdef NACL
int mosh_main( int argc, char *argv[] ) int mosh_main( int argc, char* argv[] )
#else #else
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
#endif #endif
{ {
unsigned int verbose = 0; unsigned int verbose = 0;
@@ -117,18 +120,18 @@ int main( int argc, char *argv[] )
/* Get arguments */ /* Get arguments */
for ( int i = 1; i < argc; i++ ) { for ( int i = 1; i < argc; i++ ) {
if ( 0 == strcmp( argv[ i ], "--help" ) ) { if ( 0 == strcmp( argv[i], "--help" ) ) {
print_usage( stdout, argv[ 0 ] ); print_usage( stdout, argv[0] );
exit( 0 ); exit( 0 );
} }
if ( 0 == strcmp( argv[ i ], "--version" ) ) { if ( 0 == strcmp( argv[i], "--version" ) ) {
print_version( stdout ); print_version( stdout );
exit( 0 ); exit( 0 );
} }
} }
int opt; int opt;
while ( (opt = getopt( argc, argv, "#:cv" )) != -1 ) { while ( ( opt = getopt( argc, argv, "#:cv" ) ) != -1 ) {
switch ( opt ) { switch ( opt ) {
case '#': case '#':
// Ignore the original arguments to mosh wrapper // Ignore the original arguments to mosh wrapper
@@ -141,7 +144,7 @@ int main( int argc, char *argv[] )
verbose++; verbose++;
break; break;
default: default:
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
break; break;
} }
@@ -150,34 +153,33 @@ int main( int argc, char *argv[] )
char *ip, *desired_port; char *ip, *desired_port;
if ( argc - optind != 2 ) { if ( argc - optind != 2 ) {
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
} }
ip = argv[ optind ]; ip = argv[optind];
desired_port = argv[ optind + 1 ]; desired_port = argv[optind + 1];
/* Sanity-check arguments */ /* Sanity-check arguments */
if ( desired_port if ( desired_port && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) {
&& ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) { fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[0], desired_port );
fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[ 0 ], desired_port ); print_usage( stderr, argv[0] );
print_usage( stderr, argv[ 0 ] );
exit( 1 ); exit( 1 );
} }
/* Read key from environment */ /* Read key from environment */
char *env_key = getenv( "MOSH_KEY" ); char* env_key = getenv( "MOSH_KEY" );
if ( env_key == NULL ) { if ( env_key == NULL ) {
fputs( "MOSH_KEY environment variable not found.\n", stderr ); fputs( "MOSH_KEY environment variable not found.\n", stderr );
exit( 1 ); exit( 1 );
} }
/* Read prediction preference */ /* Read prediction preference */
char *predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" ); char* predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" );
/* can be NULL */ /* can be NULL */
/* Read prediction insertion preference */ /* Read prediction insertion preference */
char *predict_overwrite = getenv( "MOSH_PREDICTION_OVERWRITE" ); char* predict_overwrite = getenv( "MOSH_PREDICTION_OVERWRITE" );
/* can be NULL */ /* can be NULL */
std::string key( env_key ); std::string key( env_key );
@@ -203,15 +205,13 @@ int main( int argc, char *argv[] )
} }
client.shutdown(); client.shutdown();
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
fprintf( stderr, "Network exception: %s\r\n", fprintf( stderr, "Network exception: %s\r\n", e.what() );
e.what() );
success = false; success = false;
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
fprintf( stderr, "Crypto exception: %s\r\n", fprintf( stderr, "Crypto exception: %s\r\n", e.what() );
e.what() );
success = false; success = false;
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
success = false; success = false;
} }
+178 -177
View File
@@ -81,14 +81,13 @@
#endif #endif
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/util/swrite.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
#include "src/util/timestamp.h" #include "src/util/timestamp.h"
#include "src/util/fatal_assert.h"
#ifndef _PATH_BSHELL #ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh" #define _PATH_BSHELL "/bin/sh"
@@ -100,34 +99,40 @@ using ServerConnection = Network::Transport<Terminal::Complete, Network::UserStr
static void serve( int host_fd, static void serve( int host_fd,
int pipe_fd, int pipe_fd,
Terminal::Complete &terminal, Terminal::Complete& terminal,
ServerConnection &network, ServerConnection& network,
long network_timeout, long network_timeout,
long network_signaled_timeout ); long network_signaled_timeout );
static int run_server( const char *desired_ip, const char *desired_port, static int run_server( const char* desired_ip,
const std::string &command_path, char *command_argv[], const char* desired_port,
const int colors, unsigned int verbose, bool with_motd ); const std::string& command_path,
char* command_argv[],
const int colors,
unsigned int verbose,
bool with_motd );
static void print_version( FILE* file )
static void print_version( FILE *file )
{ {
fputs( "mosh-server (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n" fputs( "mosh-server (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n"
"Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n" "This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n", file ); "There is NO WARRANTY, to the extent permitted by law.\n",
file );
} }
static void print_usage( FILE *stream, const char *argv0 ) static void print_usage( FILE* stream, const char* argv0 )
{ {
fprintf( stream, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT[:PORT2]] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 ); fprintf( stream,
"Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT[:PORT2]] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n",
argv0 );
} }
static bool print_motd( const char *filename ); static bool print_motd( const char* filename );
static void chdir_homedir( void ); static void chdir_homedir( void );
static bool motd_hushed( void ); static bool motd_hushed( void );
static void warn_unattached( const std::string & ignore_entry ); static void warn_unattached( const std::string& ignore_entry );
/* Simple spinloop */ /* Simple spinloop */
static void spin( void ) static void spin( void )
@@ -146,7 +151,7 @@ static void spin( void )
static std::string get_SSH_IP( void ) static std::string get_SSH_IP( void )
{ {
const char *SSH_CONNECTION = getenv( "SSH_CONNECTION" ); const char* SSH_CONNECTION = getenv( "SSH_CONNECTION" );
if ( !SSH_CONNECTION ) { /* Older sshds don't set this */ if ( !SSH_CONNECTION ) { /* Older sshds don't set this */
fputs( "Warning: SSH_CONNECTION not found; binding to any interface.\n", stderr ); fputs( "Warning: SSH_CONNECTION not found; binding to any interface.\n", stderr );
return std::string( "" ); return std::string( "" );
@@ -170,7 +175,7 @@ static std::string get_SSH_IP( void )
return local_interface_IP; return local_interface_IP;
} }
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
/* For security, make sure we don't dump core */ /* For security, make sure we don't dump core */
Crypto::disable_dumping_core(); Crypto::disable_dumping_core();
@@ -178,11 +183,11 @@ int main( int argc, char *argv[] )
/* Detect edge case */ /* Detect edge case */
fatal_assert( argc > 0 ); fatal_assert( argc > 0 );
const char *desired_ip = NULL; const char* desired_ip = NULL;
std::string desired_ip_str; std::string desired_ip_str;
const char *desired_port = NULL; const char* desired_port = NULL;
std::string command_path; std::string command_path;
char **command_argv = NULL; char** command_argv = NULL;
int colors = 0; int colors = 0;
unsigned int verbose = 0; /* don't close stdin/stdout/stderr */ unsigned int verbose = 0; /* don't close stdin/stdout/stderr */
/* Will cause mosh-server not to correctly detach on old versions of sshd. */ /* Will cause mosh-server not to correctly detach on old versions of sshd. */
@@ -190,15 +195,15 @@ int main( int argc, char *argv[] )
/* strip off command */ /* strip off command */
for ( int i = 1; i < argc; i++ ) { for ( int i = 1; i < argc; i++ ) {
if ( 0 == strcmp( argv[ i ], "--help" ) || 0 == strcmp( argv[ i ], "-h" ) ) { if ( 0 == strcmp( argv[i], "--help" ) || 0 == strcmp( argv[i], "-h" ) ) {
print_usage( stdout, argv[ 0 ] ); print_usage( stdout, argv[0] );
exit( 0 ); exit( 0 );
} }
if ( 0 == strcmp( argv[ i ], "--version" ) ) { if ( 0 == strcmp( argv[i], "--version" ) ) {
print_version( stdout ); print_version( stdout );
exit( 0 ); exit( 0 );
} }
if ( 0 == strcmp( argv[ i ], "--" ) ) { /* -- is mandatory */ if ( 0 == strcmp( argv[i], "--" ) ) { /* -- is mandatory */
if ( i != argc - 1 ) { if ( i != argc - 1 ) {
command_argv = argv + i + 1; command_argv = argv + i + 1;
} }
@@ -208,11 +213,10 @@ int main( int argc, char *argv[] )
} }
/* Parse new command-line syntax */ /* Parse new command-line syntax */
if ( (argc >= 2) if ( ( argc >= 2 ) && ( strcmp( argv[1], "new" ) == 0 ) ) {
&& (strcmp( argv[ 1 ], "new" ) == 0) ) {
/* new option syntax */ /* new option syntax */
int opt; int opt;
while ( (opt = getopt( argc - 1, argv + 1, "@:i:p:c:svl:" )) != -1 ) { while ( ( opt = getopt( argc - 1, argv + 1, "@:i:p:c:svl:" ) ) != -1 ) {
switch ( opt ) { switch ( opt ) {
/* /*
* This undocumented option does nothing but eat its argument. * This undocumented option does nothing but eat its argument.
@@ -241,9 +245,9 @@ int main( int argc, char *argv[] )
case 'c': case 'c':
try { try {
colors = myatoi( optarg ); colors = myatoi( optarg );
} catch ( const CryptoException & ) { } catch ( const CryptoException& ) {
fprintf( stderr, "%s: Bad number of colors (%s)\n", argv[ 0 ], optarg ); fprintf( stderr, "%s: Bad number of colors (%s)\n", argv[0], optarg );
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
} }
break; break;
@@ -255,7 +259,7 @@ int main( int argc, char *argv[] )
break; break;
default: default:
/* don't die on unknown options */ /* don't die on unknown options */
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
break; break;
} }
} }
@@ -263,37 +267,37 @@ int main( int argc, char *argv[] )
/* legacy argument parsing for older client wrapper script */ /* legacy argument parsing for older client wrapper script */
/* do nothing */ /* do nothing */
} else if ( argc == 2 ) { } else if ( argc == 2 ) {
desired_ip = argv[ 1 ]; desired_ip = argv[1];
} else if ( argc == 3 ) { } else if ( argc == 3 ) {
desired_ip = argv[ 1 ]; desired_ip = argv[1];
desired_port = argv[ 2 ]; desired_port = argv[2];
} else { } else {
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
} }
/* Sanity-check arguments */ /* Sanity-check arguments */
int dpl, dph; int dpl, dph;
if ( desired_port && ! Connection::parse_portrange( desired_port, dpl, dph ) ) { if ( desired_port && !Connection::parse_portrange( desired_port, dpl, dph ) ) {
fprintf( stderr, "%s: Bad UDP port range (%s)\n", argv[ 0 ], desired_port ); fprintf( stderr, "%s: Bad UDP port range (%s)\n", argv[0], desired_port );
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
} }
bool with_motd = false; bool with_motd = false;
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_AUTH); openlog( argv[0], LOG_PID | LOG_NDELAY, LOG_AUTH );
#endif #endif
/* Get shell */ /* Get shell */
char *my_argv[ 2 ]; char* my_argv[2];
std::string shell_name; std::string shell_name;
if ( !command_argv ) { if ( !command_argv ) {
/* get shell name */ /* get shell name */
const char *shell = getenv( "SHELL" ); const char* shell = getenv( "SHELL" );
if ( shell == NULL ) { if ( shell == NULL ) {
struct passwd *pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if ( pw == NULL ) { if ( pw == NULL ) {
perror( "getpwuid" ); perror( "getpwuid" );
exit( 1 ); exit( 1 );
@@ -308,18 +312,18 @@ int main( int argc, char *argv[] )
command_path = shell_path; command_path = shell_path;
size_t shell_slash( shell_path.rfind('/') ); size_t shell_slash( shell_path.rfind( '/' ) );
if ( shell_slash == std::string::npos ) { if ( shell_slash == std::string::npos ) {
shell_name = shell_path; shell_name = shell_path;
} else { } else {
shell_name = shell_path.substr(shell_slash + 1); shell_name = shell_path.substr( shell_slash + 1 );
} }
/* prepend '-' to make login shell */ /* prepend '-' to make login shell */
shell_name = '-' + shell_name; shell_name = '-' + shell_name;
my_argv[ 0 ] = const_cast<char *>( shell_name.c_str() ); my_argv[0] = const_cast<char*>( shell_name.c_str() );
my_argv[ 1 ] = NULL; my_argv[1] = NULL;
command_argv = my_argv; command_argv = my_argv;
with_motd = true; with_motd = true;
@@ -338,10 +342,8 @@ int main( int argc, char *argv[] )
/* apply locale-related environment variables from client */ /* apply locale-related environment variables from client */
clear_locale_variables(); clear_locale_variables();
for ( std::list<std::string>::const_iterator i = locale_vars.begin(); for ( std::list<std::string>::const_iterator i = locale_vars.begin(); i != locale_vars.end(); i++ ) {
i != locale_vars.end(); char* env_string = strdup( i->c_str() );
i++ ) {
char *env_string = strdup( i->c_str() );
fatal_assert( env_string ); fatal_assert( env_string );
if ( 0 != putenv( env_string ) ) { if ( 0 != putenv( env_string ) ) {
perror( "putenv" ); perror( "putenv" );
@@ -354,39 +356,46 @@ int main( int argc, char *argv[] )
LocaleVar client_ctype = get_ctype(); LocaleVar client_ctype = get_ctype();
std::string client_charset( locale_charset() ); std::string client_charset( locale_charset() );
fprintf( stderr, "mosh-server needs a UTF-8 native locale to run.\n\n" fprintf( stderr,
"mosh-server needs a UTF-8 native locale to run.\n\n"
"Unfortunately, the local environment (%s) specifies\n" "Unfortunately, the local environment (%s) specifies\n"
"the character set \"%s\",\n\n" "the character set \"%s\",\n\n"
"The client-supplied environment (%s) specifies\n" "The client-supplied environment (%s) specifies\n"
"the character set \"%s\".\n\n", "the character set \"%s\".\n\n",
native_ctype.str().c_str(), native_charset.c_str(), client_ctype.str().c_str(), client_charset.c_str() ); native_ctype.str().c_str(),
int unused __attribute((unused)) = system( "locale" ); native_charset.c_str(),
client_ctype.str().c_str(),
client_charset.c_str() );
int unused __attribute( ( unused ) ) = system( "locale" );
exit( 1 ); exit( 1 );
} }
} }
try { try {
return run_server( desired_ip, desired_port, command_path, command_argv, colors, verbose, with_motd ); return run_server( desired_ip, desired_port, command_path, command_argv, colors, verbose, with_motd );
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
fprintf( stderr, "Network exception: %s\n", fprintf( stderr, "Network exception: %s\n", e.what() );
e.what() );
return 1; return 1;
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
fprintf( stderr, "Crypto exception: %s\n", fprintf( stderr, "Crypto exception: %s\n", e.what() );
e.what() );
return 1; return 1;
} }
} }
static int run_server( const char *desired_ip, const char *desired_port, static int run_server( const char* desired_ip,
const std::string &command_path, char *command_argv[], const char* desired_port,
const int colors, unsigned int verbose, bool with_motd ) { const std::string& command_path,
char* command_argv[],
const int colors,
unsigned int verbose,
bool with_motd )
{
/* get network idle timeout */ /* get network idle timeout */
long network_timeout = 0; long network_timeout = 0;
char *timeout_envar = getenv( "MOSH_SERVER_NETWORK_TMOUT" ); char* timeout_envar = getenv( "MOSH_SERVER_NETWORK_TMOUT" );
if ( timeout_envar && *timeout_envar ) { if ( timeout_envar && *timeout_envar ) {
errno = 0; errno = 0;
char *endptr; char* endptr;
network_timeout = strtol( timeout_envar, &endptr, 10 ); network_timeout = strtol( timeout_envar, &endptr, 10 );
if ( *endptr != '\0' || ( network_timeout == 0 && errno == EINVAL ) ) { if ( *endptr != '\0' || ( network_timeout == 0 && errno == EINVAL ) ) {
fputs( "MOSH_SERVER_NETWORK_TMOUT not a valid integer, ignoring\n", stderr ); fputs( "MOSH_SERVER_NETWORK_TMOUT not a valid integer, ignoring\n", stderr );
@@ -397,10 +406,10 @@ static int run_server( const char *desired_ip, const char *desired_port,
} }
/* get network signaled idle timeout */ /* get network signaled idle timeout */
long network_signaled_timeout = 0; long network_signaled_timeout = 0;
char *signal_envar = getenv( "MOSH_SERVER_SIGNAL_TMOUT" ); char* signal_envar = getenv( "MOSH_SERVER_SIGNAL_TMOUT" );
if ( signal_envar && *signal_envar ) { if ( signal_envar && *signal_envar ) {
errno = 0; errno = 0;
char *endptr; char* endptr;
network_signaled_timeout = strtol( signal_envar, &endptr, 10 ); network_signaled_timeout = strtol( signal_envar, &endptr, 10 );
if ( *endptr != '\0' || ( network_signaled_timeout == 0 && errno == EINVAL ) ) { if ( *endptr != '\0' || ( network_signaled_timeout == 0 && errno == EINVAL ) ) {
fputs( "MOSH_SERVER_SIGNAL_TMOUT not a valid integer, ignoring\n", stderr ); fputs( "MOSH_SERVER_SIGNAL_TMOUT not a valid integer, ignoring\n", stderr );
@@ -411,9 +420,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
} }
/* get initial window size */ /* get initial window size */
struct winsize window_size; struct winsize window_size;
if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 || if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 || window_size.ws_col == 0 || window_size.ws_row == 0 ) {
window_size.ws_col == 0 ||
window_size.ws_row == 0 ) {
/* Fill in sensible defaults. */ /* Fill in sensible defaults. */
/* They will be overwritten by client on first connection. */ /* They will be overwritten by client on first connection. */
memset( &window_size, 0, sizeof( window_size ) ); memset( &window_size, 0, sizeof( window_size ) );
@@ -450,7 +457,6 @@ static int run_server( const char *desired_ip, const char *desired_port,
fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) );
fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) );
/* detach from terminal */ /* detach from terminal */
fflush( NULL ); fflush( NULL );
pid_t the_pid = fork(); pid_t the_pid = fork();
@@ -461,13 +467,15 @@ static int run_server( const char *desired_ip, const char *desired_port,
"Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n" "This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n\n", stderr ); "There is NO WARRANTY, to the extent permitted by law.\n\n",
stderr );
fprintf( stderr, "[mosh-server detached, pid = %d]\n", static_cast<int>(the_pid) ); fprintf( stderr, "[mosh-server detached, pid = %d]\n", static_cast<int>( the_pid ) );
#ifndef HAVE_IUTF8 #ifndef HAVE_IUTF8
fputs( "\nWarning: termios IUTF8 flag not defined.\n" fputs( "\nWarning: termios IUTF8 flag not defined.\n"
"Character-erase of multibyte character sequence\n" "Character-erase of multibyte character sequence\n"
"probably does not work properly on this platform.\n", stderr ); "probably does not work properly on this platform.\n",
stderr );
#endif /* HAVE_IUTF8 */ #endif /* HAVE_IUTF8 */
fflush( NULL ); fflush( NULL );
@@ -493,9 +501,8 @@ static int run_server( const char *desired_ip, const char *desired_port,
exit( 1 ); exit( 1 );
} }
if ( dup2 ( nullfd, STDIN_FILENO ) < 0 || if ( dup2( nullfd, STDIN_FILENO ) < 0 || dup2( nullfd, STDOUT_FILENO ) < 0
dup2 ( nullfd, STDOUT_FILENO ) < 0 || || dup2( nullfd, STDERR_FILENO ) < 0 ) {
dup2 ( nullfd, STDERR_FILENO ) < 0 ) {
perror( "dup2" ); perror( "dup2" );
exit( 1 ); exit( 1 );
} }
@@ -506,13 +513,13 @@ static int run_server( const char *desired_ip, const char *desired_port,
} }
} }
char utmp_entry[ 64 ] = { 0 }; char utmp_entry[64] = { 0 };
snprintf( utmp_entry, 64, "mosh [%ld]", static_cast<long int>( getpid() ) ); snprintf( utmp_entry, 64, "mosh [%ld]", static_cast<long int>( getpid() ) );
/* Fork child process */ /* Fork child process */
int pipes[2]; int pipes[2];
int success = pipe(pipes); int success = pipe( pipes );
if (success == -1) { if ( success == -1 ) {
perror( "pipe" ); perror( "pipe" );
exit( 1 ); exit( 1 );
} }
@@ -538,9 +545,9 @@ static int run_server( const char *desired_ip, const char *desired_port,
fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) );
fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) ); fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) );
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
closelog(); closelog();
#endif #endif
/* close server-related file descriptors */ /* close server-related file descriptors */
network.reset(); network.reset();
@@ -565,7 +572,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
const char default_term[] = "xterm"; const char default_term[] = "xterm";
const char color_term[] = "xterm-256color"; const char color_term[] = "xterm-256color";
if ( setenv( "TERM", (colors == 256) ? color_term : default_term, true ) < 0 ) { if ( setenv( "TERM", ( colors == 256 ) ? color_term : default_term, true ) < 0 ) {
perror( "setenv" ); perror( "setenv" );
exit( 1 ); exit( 1 );
} }
@@ -584,7 +591,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
chdir_homedir(); chdir_homedir();
if ( with_motd && (!motd_hushed()) ) { if ( with_motd && ( !motd_hushed() ) ) {
// On illumos motd is printed by /etc/profile. // On illumos motd is printed by /etc/profile.
#ifndef __sun #ifndef __sun
// For Ubuntu, try and print one of {,/var}/run/motd.dynamic. // For Ubuntu, try and print one of {,/var}/run/motd.dynamic.
@@ -593,11 +600,11 @@ static int run_server( const char *desired_ip, const char *desired_port,
// this always happens. // this always happens.
// XXX Hackish knowledge of Ubuntu PAM configuration. // XXX Hackish knowledge of Ubuntu PAM configuration.
// But this seems less awful than build-time detection with autoconf. // But this seems less awful than build-time detection with autoconf.
if (!print_motd("/run/motd.dynamic")) { if ( !print_motd( "/run/motd.dynamic" ) ) {
print_motd("/var/run/motd.dynamic"); print_motd( "/var/run/motd.dynamic" );
} }
// Always print traditional /etc/motd. // Always print traditional /etc/motd.
print_motd("/etc/motd"); print_motd( "/etc/motd" );
#endif #endif
warn_unattached( utmp_entry ); warn_unattached( utmp_entry );
} }
@@ -626,7 +633,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
} }
} else { } else {
/* parent */ /* parent */
if ( close(pipes[0]) < 0 ) { if ( close( pipes[0] ) < 0 ) {
perror( "parent read pipe close" ); perror( "parent read pipe close" );
exit( 1 ); exit( 1 );
} }
@@ -634,7 +641,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
/* Drop unnecessary privileges */ /* Drop unnecessary privileges */
#ifdef HAVE_PLEDGE #ifdef HAVE_PLEDGE
/* OpenBSD pledge() syscall */ /* OpenBSD pledge() syscall */
if ( pledge( "stdio inet tty", NULL )) { if ( pledge( "stdio inet tty", NULL ) ) {
perror( "pledge() failed" ); perror( "pledge() failed" );
exit( 1 ); exit( 1 );
} }
@@ -647,17 +654,15 @@ static int run_server( const char *desired_ip, const char *desired_port,
try { try {
serve( master, pipes[1], terminal, *network, network_timeout, network_signaled_timeout ); serve( master, pipes[1], terminal, *network, network_timeout, network_signaled_timeout );
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
fprintf( stderr, "Network exception: %s\n", fprintf( stderr, "Network exception: %s\n", e.what() );
e.what() ); } catch ( const Crypto::CryptoException& e ) {
} catch ( const Crypto::CryptoException &e ) { fprintf( stderr, "Crypto exception: %s\n", e.what() );
fprintf( stderr, "Crypto exception: %s\n",
e.what() );
} }
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
utempter_remove_record( master ); utempter_remove_record( master );
#endif #endif
if ( close( master ) < 0 ) { if ( close( master ) < 0 ) {
perror( "close" ); perror( "close" );
@@ -670,35 +675,40 @@ static int run_server( const char *desired_ip, const char *desired_port,
return 0; return 0;
} }
static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, ServerConnection &network, long network_timeout, long network_signaled_timeout ) static void serve( int host_fd,
int pipe_fd,
Terminal::Complete& terminal,
ServerConnection& network,
long network_timeout,
long network_signaled_timeout )
{ {
/* scale timeouts */ /* scale timeouts */
const uint64_t network_timeout_ms = static_cast<uint64_t>( network_timeout ) * 1000; const uint64_t network_timeout_ms = static_cast<uint64_t>( network_timeout ) * 1000;
const uint64_t network_signaled_timeout_ms = static_cast<uint64_t>( network_signaled_timeout ) * 1000; const uint64_t network_signaled_timeout_ms = static_cast<uint64_t>( network_signaled_timeout ) * 1000;
/* prepare to poll for events */ /* prepare to poll for events */
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_signal( SIGTERM ); sel.add_signal( SIGTERM );
sel.add_signal( SIGINT ); sel.add_signal( SIGINT );
sel.add_signal( SIGUSR1 ); sel.add_signal( SIGUSR1 );
uint64_t last_remote_num = network.get_remote_state_num(); uint64_t last_remote_num = network.get_remote_state_num();
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
bool connected_utmp = false; bool connected_utmp = false;
#endif #endif
#if defined(HAVE_SYSLOG) || defined(HAVE_UTEMPTER) #if defined( HAVE_SYSLOG ) || defined( HAVE_UTEMPTER )
bool force_connection_change_evt = false; bool force_connection_change_evt = false;
Addr saved_addr; Addr saved_addr;
socklen_t saved_addr_len = 0; socklen_t saved_addr_len = 0;
#endif #endif
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
struct passwd *pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if (pw == NULL) { if ( pw == NULL ) {
throw NetworkException( std::string( "serve: getpwuid: " ) + strerror( errno ), 0 ); throw NetworkException( std::string( "serve: getpwuid: " ) + strerror( errno ), 0 );
} }
syslog(LOG_INFO, "user %s session begin", pw->pw_name); syslog( LOG_INFO, "user %s session begin", pw->pw_name );
#endif #endif
bool child_released = false; bool child_released = false;
@@ -710,8 +720,7 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
timeout = std::min( timeout, network.wait_time() ); timeout = std::min( timeout, network.wait_time() );
timeout = std::min( timeout, terminal.wait_time( now ) ); timeout = std::min( timeout, terminal.wait_time( now ) );
if ( (!network.get_remote_state_num()) if ( ( !network.get_remote_state_num() ) || network.shutdown_in_progress() ) {
|| network.shutdown_in_progress() ) {
timeout = std::min( timeout, 5000 ); timeout = std::min( timeout, 5000 );
} }
/* /*
@@ -719,20 +728,19 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
* We may want to wake up sooner. * We may want to wake up sooner.
*/ */
if ( network_timeout_ms ) { if ( network_timeout_ms ) {
int64_t network_sleep = network_timeout_ms - int64_t network_sleep = network_timeout_ms - ( now - network.get_latest_remote_state().timestamp );
( now - network.get_latest_remote_state().timestamp );
if ( network_sleep < 0 ) { if ( network_sleep < 0 ) {
network_sleep = 0; network_sleep = 0;
} else if ( network_sleep > INT_MAX ) { } else if ( network_sleep > INT_MAX ) {
/* 24 days might be too soon. That's OK. */ /* 24 days might be too soon. That's OK. */
network_sleep = INT_MAX; network_sleep = INT_MAX;
} }
timeout = std::min( timeout, static_cast<int>(network_sleep) ); timeout = std::min( timeout, static_cast<int>( network_sleep ) );
} }
/* poll for events */ /* poll for events */
sel.clear_fds(); sel.clear_fds();
std::vector< int > fd_list( network.fds() ); std::vector<int> fd_list( network.fds() );
assert( fd_list.size() == 1 ); /* servers don't hop */ assert( fd_list.size() == 1 ); /* servers don't hop */
int network_fd = fd_list.back(); int network_fd = fd_list.back();
sel.add_fd( network_fd ); sel.add_fd( network_fd );
@@ -758,22 +766,21 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
if ( network.get_remote_state_num() != last_remote_num ) { if ( network.get_remote_state_num() != last_remote_num ) {
last_remote_num = network.get_remote_state_num(); last_remote_num = network.get_remote_state_num();
Network::UserStream us; Network::UserStream us;
us.apply_string( network.get_remote_diff() ); us.apply_string( network.get_remote_diff() );
/* apply userstream to terminal */ /* apply userstream to terminal */
for ( size_t i = 0; i < us.size(); i++ ) { for ( size_t i = 0; i < us.size(); i++ ) {
const Parser::Action &action = us.get_action( i ); const Parser::Action& action = us.get_action( i );
if ( typeid( action ) == typeid( Parser::Resize ) ) { if ( typeid( action ) == typeid( Parser::Resize ) ) {
/* apply only the last consecutive Resize action */ /* apply only the last consecutive Resize action */
if ( i < us.size() - 1 ) { if ( i < us.size() - 1 ) {
const Parser::Action &next = us.get_action( i + 1 ); const Parser::Action& next = us.get_action( i + 1 );
if ( typeid( next ) == typeid( Parser::Resize ) ) { if ( typeid( next ) == typeid( Parser::Resize ) ) {
continue; continue;
} }
} }
/* tell child process of resize */ /* tell child process of resize */
const Parser::Resize &res = static_cast<const Parser::Resize &>( action ); const Parser::Resize& res = static_cast<const Parser::Resize&>( action );
struct winsize window_size; struct winsize window_size;
if ( ioctl( host_fd, TIOCGWINSZ, &window_size ) < 0 ) { if ( ioctl( host_fd, TIOCGWINSZ, &window_size ) < 0 ) {
perror( "ioctl TIOCGWINSZ" ); perror( "ioctl TIOCGWINSZ" );
@@ -798,51 +805,48 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
if ( !network.shutdown_in_progress() ) { if ( !network.shutdown_in_progress() ) {
network.set_current_state( terminal ); network.set_current_state( terminal );
} }
#if defined(HAVE_SYSLOG) || defined(HAVE_UTEMPTER) #if defined( HAVE_SYSLOG ) || defined( HAVE_UTEMPTER )
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
if (!connected_utmp) { if ( !connected_utmp ) {
force_connection_change_evt = true; force_connection_change_evt = true;
} else { } else {
force_connection_change_evt = false; force_connection_change_evt = false;
} }
#else #else
force_connection_change_evt = false; force_connection_change_evt = false;
#endif #endif
/** /**
* - HAVE_UTEMPTER - update utmp entry if we have become "connected" * - HAVE_UTEMPTER - update utmp entry if we have become "connected"
* - HAVE_SYSLOG - log connection information to syslog * - HAVE_SYSLOG - log connection information to syslog
**/ **/
if ( (force_connection_change_evt) if ( ( force_connection_change_evt ) || saved_addr_len != network.get_remote_addr_len()
|| saved_addr_len != network.get_remote_addr_len() || memcmp( &saved_addr, &network.get_remote_addr(), saved_addr_len ) != 0 ) {
|| memcmp( &saved_addr, &network.get_remote_addr(),
saved_addr_len ) != 0 ) {
saved_addr = network.get_remote_addr(); saved_addr = network.get_remote_addr();
saved_addr_len = network.get_remote_addr_len(); saved_addr_len = network.get_remote_addr_len();
char host[ NI_MAXHOST ]; char host[NI_MAXHOST];
int errcode = getnameinfo( &saved_addr.sa, saved_addr_len, int errcode
host, sizeof( host ), NULL, 0, = getnameinfo( &saved_addr.sa, saved_addr_len, host, sizeof( host ), NULL, 0, NI_NUMERICHOST );
NI_NUMERICHOST );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "serve: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "serve: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
utempter_remove_record( host_fd ); utempter_remove_record( host_fd );
char tmp[ 64 + NI_MAXHOST ]; char tmp[64 + NI_MAXHOST];
snprintf( tmp, 64 + NI_MAXHOST, "%s via mosh [%ld]", host, static_cast<long int>( getpid() ) ); snprintf( tmp, 64 + NI_MAXHOST, "%s via mosh [%ld]", host, static_cast<long int>( getpid() ) );
utempter_add_record( host_fd, tmp ); utempter_add_record( host_fd, tmp );
connected_utmp = true; connected_utmp = true;
#endif #endif
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
syslog(LOG_INFO, "user %s connected from host: %s", pw->pw_name, host); syslog( LOG_INFO, "user %s connected from host: %s", pw->pw_name, host );
#endif #endif
} }
#endif #endif
/* Tell child to start login session. */ /* Tell child to start login session. */
if ( !child_released ) { if ( !child_released ) {
@@ -854,10 +858,10 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
} }
} }
if ( (!network.shutdown_in_progress()) && sel.read( host_fd ) ) { if ( ( !network.shutdown_in_progress() ) && sel.read( host_fd ) ) {
/* input from the host needs to be fed to the terminal */ /* input from the host needs to be fed to the terminal */
const int buf_size = 16384; const int buf_size = 16384;
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( host_fd, buf, buf_size ); ssize_t bytes_read = read( host_fd, buf, buf_size );
@@ -880,22 +884,23 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
} }
bool idle_shutdown = false; bool idle_shutdown = false;
if ( network_timeout_ms && if ( network_timeout_ms && network_timeout_ms <= time_since_remote_state ) {
network_timeout_ms <= time_since_remote_state ) {
idle_shutdown = true; idle_shutdown = true;
fprintf( stderr, "Network idle for %llu seconds.\n", fprintf( stderr,
"Network idle for %llu seconds.\n",
static_cast<unsigned long long>( time_since_remote_state / 1000 ) ); static_cast<unsigned long long>( time_since_remote_state / 1000 ) );
} }
if ( sel.signal( SIGUSR1 ) if ( sel.signal( SIGUSR1 )
&& ( !network_signaled_timeout_ms || network_signaled_timeout_ms <= time_since_remote_state ) ) { && ( !network_signaled_timeout_ms || network_signaled_timeout_ms <= time_since_remote_state ) ) {
idle_shutdown = true; idle_shutdown = true;
fprintf( stderr, "Network idle for %llu seconds when SIGUSR1 received\n", fprintf( stderr,
"Network idle for %llu seconds when SIGUSR1 received\n",
static_cast<unsigned long long>( time_since_remote_state / 1000 ) ); static_cast<unsigned long long>( time_since_remote_state / 1000 ) );
} }
if ( sel.any_signal() || idle_shutdown ) { if ( sel.any_signal() || idle_shutdown ) {
/* shutdown signal */ /* shutdown signal */
if ( network.has_remote_addr() && (!network.shutdown_in_progress()) ) { if ( network.has_remote_addr() && ( !network.shutdown_in_progress() ) ) {
network.start_shutdown(); network.start_shutdown();
} else { } else {
break; break;
@@ -917,37 +922,36 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
break; break;
} }
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
/* update utmp if has been more than 30 seconds since heard from client */ /* update utmp if has been more than 30 seconds since heard from client */
if ( connected_utmp if ( connected_utmp && time_since_remote_state > 30000 ) {
&& time_since_remote_state > 30000 ) {
utempter_remove_record( host_fd ); utempter_remove_record( host_fd );
char tmp[ 64 ]; char tmp[64];
snprintf( tmp, 64, "mosh [%ld]", static_cast<long int>( getpid() ) ); snprintf( tmp, 64, "mosh [%ld]", static_cast<long int>( getpid() ) );
utempter_add_record( host_fd, tmp ); utempter_add_record( host_fd, tmp );
connected_utmp = false; connected_utmp = false;
} }
#endif #endif
if ( terminal.set_echo_ack( now ) && !network.shutdown_in_progress() ) { if ( terminal.set_echo_ack( now ) && !network.shutdown_in_progress() ) {
/* update client with new echo ack */ /* update client with new echo ack */
network.set_current_state( terminal ); network.set_current_state( terminal );
} }
if ( !network.get_remote_state_num() if ( !network.get_remote_state_num() && time_since_remote_state >= timeout_if_no_client ) {
&& time_since_remote_state >= timeout_if_no_client ) { fprintf( stderr,
fprintf( stderr, "No connection within %llu seconds.\n", "No connection within %llu seconds.\n",
static_cast<unsigned long long>( timeout_if_no_client / 1000 ) ); static_cast<unsigned long long>( timeout_if_no_client / 1000 ) );
break; break;
} }
network.tick(); network.tick();
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
fprintf( stderr, "%s\n", e.what() ); fprintf( stderr, "%s\n", e.what() );
spin(); spin();
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
if ( e.fatal ) { if ( e.fatal ) {
throw; throw;
} else { } else {
@@ -955,22 +959,22 @@ static void serve( int host_fd, int pipe_fd, Terminal::Complete &terminal, Serve
} }
} }
} }
#ifdef HAVE_SYSLOG #ifdef HAVE_SYSLOG
syslog(LOG_INFO, "user %s session end", pw->pw_name); syslog( LOG_INFO, "user %s session end", pw->pw_name );
#endif #endif
} }
/* Print the motd from a given file, if available */ /* Print the motd from a given file, if available */
static bool print_motd( const char *filename ) static bool print_motd( const char* filename )
{ {
FILE *motd = fopen( filename, "r" ); FILE* motd = fopen( filename, "r" );
if ( !motd ) { if ( !motd ) {
return false; return false;
} }
const int BUFSIZE = 256; const int BUFSIZE = 256;
char buffer[ BUFSIZE ]; char buffer[BUFSIZE];
while ( 1 ) { while ( 1 ) {
size_t bytes_read = fread( buffer, 1, BUFSIZE, motd ); size_t bytes_read = fread( buffer, 1, BUFSIZE, motd );
if ( bytes_read == 0 ) { if ( bytes_read == 0 ) {
@@ -988,9 +992,9 @@ static bool print_motd( const char *filename )
static void chdir_homedir( void ) static void chdir_homedir( void )
{ {
const char *home = getenv( "HOME" ); const char* home = getenv( "HOME" );
if ( home == NULL ) { if ( home == NULL ) {
struct passwd *pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if ( pw == NULL ) { if ( pw == NULL ) {
perror( "getpwuid" ); perror( "getpwuid" );
return; /* non-fatal */ return; /* non-fatal */
@@ -1015,7 +1019,7 @@ static bool motd_hushed( void )
} }
#ifdef HAVE_UTMPX_H #ifdef HAVE_UTMPX_H
static bool device_exists( const char *ut_line ) static bool device_exists( const char* ut_line )
{ {
std::string device_name = std::string( "/dev/" ) + std::string( ut_line ); std::string device_name = std::string( "/dev/" ) + std::string( ut_line );
struct stat buf; struct stat buf;
@@ -1023,11 +1027,11 @@ static bool device_exists( const char *ut_line )
} }
#endif #endif
static void warn_unattached( const std::string & ignore_entry ) static void warn_unattached( const std::string& ignore_entry )
{ {
#ifdef HAVE_UTMPX_H #ifdef HAVE_UTMPX_H
/* get username */ /* get username */
const struct passwd *pw = getpwuid( getuid() ); const struct passwd* pw = getpwuid( getuid() );
if ( pw == NULL ) { if ( pw == NULL ) {
perror( "getpwuid" ); perror( "getpwuid" );
/* non-fatal */ /* non-fatal */
@@ -1037,18 +1041,14 @@ static void warn_unattached( const std::string & ignore_entry )
const std::string username( pw->pw_name ); const std::string username( pw->pw_name );
/* look for unattached sessions */ /* look for unattached sessions */
std::vector< std::string > unattached_mosh_servers; std::vector<std::string> unattached_mosh_servers;
while ( struct utmpx *entry = getutxent() ) { while ( struct utmpx* entry = getutxent() ) {
if ( (entry->ut_type == USER_PROCESS) if ( ( entry->ut_type == USER_PROCESS ) && ( username == std::string( entry->ut_user ) ) ) {
&& (username == std::string( entry->ut_user )) ) {
/* does line show unattached mosh session */ /* does line show unattached mosh session */
std::string text( entry->ut_host ); std::string text( entry->ut_host );
if ( (text.size() >= 5) if ( ( text.size() >= 5 ) && ( text.substr( 0, 5 ) == "mosh " ) && ( text[text.size() - 1] == ']' )
&& (text.substr( 0, 5 ) == "mosh ") && ( text != ignore_entry ) && device_exists( entry->ut_line ) ) {
&& (text[ text.size() - 1 ] == ']')
&& (text != ignore_entry)
&& device_exists( entry->ut_line ) ) {
unattached_mosh_servers.push_back( text ); unattached_mosh_servers.push_back( text );
} }
} }
@@ -1063,14 +1063,15 @@ static void warn_unattached( const std::string & ignore_entry )
} else { } else {
std::string pid_string; std::string pid_string;
for ( std::vector< std::string >::const_iterator it = unattached_mosh_servers.begin(); for ( std::vector<std::string>::const_iterator it = unattached_mosh_servers.begin();
it != unattached_mosh_servers.end(); it != unattached_mosh_servers.end();
it++ ) { it++ ) {
pid_string += " - " + *it + "\n"; pid_string += " - " + *it + "\n";
} }
printf( "\033[37;44mMosh: You have %d detached Mosh sessions on this server, with PIDs:\n%s\033[m\n", printf( "\033[37;44mMosh: You have %d detached Mosh sessions on this server, with PIDs:\n%s\033[m\n",
(int)unattached_mosh_servers.size(), pid_string.c_str() ); (int)unattached_mosh_servers.size(),
pid_string.c_str() );
} }
#endif /* HAVE_UTMPX_H */ #endif /* HAVE_UTMPX_H */
} }
+64 -60
View File
@@ -52,15 +52,15 @@
#include <util.h> #include <util.h>
#endif #endif
#include "stmclient.h"
#include "src/util/swrite.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
#include "src/util/timestamp.h" #include "src/util/timestamp.h"
#include "stmclient.h"
#include "src/network/networktransport-impl.h" #include "src/network/networktransport-impl.h"
@@ -85,11 +85,13 @@ void STMClient::init( void )
LocaleVar native_ctype = get_ctype(); LocaleVar native_ctype = get_ctype();
std::string native_charset( locale_charset() ); std::string native_charset( locale_charset() );
fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n" fprintf( stderr,
"mosh-client needs a UTF-8 native locale to run.\n\n"
"Unfortunately, the client's environment (%s) specifies\n" "Unfortunately, the client's environment (%s) specifies\n"
"the character set \"%s\".\n\n", "the character set \"%s\".\n\n",
native_ctype.str().c_str(), native_charset.c_str() ); native_ctype.str().c_str(),
int unused __attribute((unused)) = system( "locale" ); native_charset.c_str() );
int unused __attribute( ( unused ) ) = system( "locale" );
exit( 1 ); exit( 1 );
} }
@@ -103,7 +105,7 @@ void STMClient::init( void )
raw_termios = saved_termios; raw_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(raw_termios.c_iflag & IUTF8) ) { if ( !( raw_termios.c_iflag & IUTF8 ) ) {
// fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); // fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
/* Probably not really necessary since we are putting terminal driver into raw mode anyway. */ /* Probably not really necessary since we are putting terminal driver into raw mode anyway. */
raw_termios.c_iflag |= IUTF8; raw_termios.c_iflag |= IUTF8;
@@ -126,8 +128,8 @@ void STMClient::init( void )
} }
/* Set terminal escape key. */ /* Set terminal escape key. */
const char *escape_key_env; const char* escape_key_env;
if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) { if ( ( escape_key_env = getenv( "MOSH_ESCAPE_KEY" ) ) != NULL ) {
if ( strlen( escape_key_env ) == 1 ) { if ( strlen( escape_key_env ) == 1 ) {
escape_key = (int)escape_key_env[0]; escape_key = (int)escape_key_env[0];
if ( escape_key > 0 && escape_key < 128 ) { if ( escape_key > 0 && escape_key < 128 ) {
@@ -166,7 +168,8 @@ void STMClient::init( void )
/* There are so many better ways to shoot oneself into leg than /* There are so many better ways to shoot oneself into leg than
setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn
that we just won't allow that. */ that we just won't allow that. */
if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) { if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C
|| escape_key == 0x0D ) {
escape_key = 0x1E; escape_key = 0x1E;
escape_pass_key = '^'; escape_pass_key = '^';
escape_pass_key2 = '^'; escape_pass_key2 = '^';
@@ -176,23 +179,24 @@ void STMClient::init( void )
if ( escape_key > 0 ) { if ( escape_key > 0 ) {
char escape_pass_name_buf[16]; char escape_pass_name_buf[16];
char escape_key_name_buf[16]; char escape_key_name_buf[16];
snprintf(escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key); snprintf( escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key );
if (escape_key < 32) { if ( escape_key < 32 ) {
snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key); snprintf( escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key );
escape_requires_lf = false; escape_requires_lf = false;
} else { } else {
snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key); snprintf( escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key );
escape_requires_lf = true; escape_requires_lf = true;
} }
std::string tmp; std::string tmp;
tmp = std::string( escape_pass_name_buf ); tmp = std::string( escape_pass_name_buf );
std::wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end()); std::wstring escape_pass_name = std::wstring( tmp.begin(), tmp.end() );
tmp = std::string( escape_key_name_buf ); tmp = std::string( escape_key_name_buf );
std::wstring escape_key_name = std::wstring(tmp.begin(), tmp.end()); std::wstring escape_key_name = std::wstring( tmp.begin(), tmp.end() );
escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name; escape_key_help
= L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name;
overlays.get_notification_engine().set_escape_key_string( tmp ); overlays.get_notification_engine().set_escape_key_string( tmp );
} }
wchar_t tmp[ 128 ]; wchar_t tmp[128];
swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() ); swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() );
connecting_notification = std::wstring( tmp ); connecting_notification = std::wstring( tmp );
} }
@@ -214,19 +218,24 @@ void STMClient::shutdown( void )
} }
if ( still_connecting() ) { if ( still_connecting() ) {
fprintf( stderr, "\nmosh did not make a successful connection to %s:%s.\n" fprintf( stderr,
"\nmosh did not make a successful connection to %s:%s.\n"
"Please verify that UDP port %s is not firewalled and can reach the server.\n\n" "Please verify that UDP port %s is not firewalled and can reach the server.\n\n"
"(By default, mosh uses a UDP port between 60000 and 61000. The -p option\n" "(By default, mosh uses a UDP port between 60000 and 61000. The -p option\n"
"selects a specific UDP port number.)\n", ip.c_str(), port.c_str(), port.c_str() ); "selects a specific UDP port number.)\n",
ip.c_str(),
port.c_str(),
port.c_str() );
} else if ( network && !clean_shutdown ) { } else if ( network && !clean_shutdown ) {
fputs( "\n\nmosh did not shut down cleanly. Please note that the\n" fputs( "\n\nmosh did not shut down cleanly. Please note that the\n"
"mosh-server process may still be running on the server.\n", stderr ); "mosh-server process may still be running on the server.\n",
stderr );
} }
} }
void STMClient::main_init( void ) void STMClient::main_init( void )
{ {
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_signal( SIGWINCH ); sel.add_signal( SIGWINCH );
sel.add_signal( SIGTERM ); sel.add_signal( SIGTERM );
sel.add_signal( SIGINT ); sel.add_signal( SIGINT );
@@ -276,9 +285,7 @@ void STMClient::output_new_frame( void )
overlays.apply( new_state ); overlays.apply( new_state );
/* calculate minimal difference from where we are */ /* calculate minimal difference from where we are */
const std::string diff( display.new_frame( !repaint_requested, const std::string diff( display.new_frame( !repaint_requested, local_framebuffer, new_state ) );
local_framebuffer,
new_state ) );
swrite( STDOUT_FILENO, diff.data(), diff.size() ); swrite( STDOUT_FILENO, diff.data(), diff.size() );
repaint_requested = false; repaint_requested = false;
@@ -296,13 +303,14 @@ void STMClient::process_network_input( void )
overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() ); overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() );
overlays.get_prediction_engine().set_send_interval( network->send_interval() ); overlays.get_prediction_engine().set_send_interval( network->send_interval() );
overlays.get_prediction_engine().set_local_frame_late_acked( network->get_latest_remote_state().state.get_echo_ack() ); overlays.get_prediction_engine().set_local_frame_late_acked(
network->get_latest_remote_state().state.get_echo_ack() );
} }
bool STMClient::process_user_input( int fd ) bool STMClient::process_user_input( int fd )
{ {
const int buf_size = 16384; const int buf_size = 16384;
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
@@ -313,7 +321,7 @@ bool STMClient::process_user_input( int fd )
return false; return false;
} }
NetworkType &net = *network; NetworkType& net = *network;
if ( net.shutdown_in_progress() ) { if ( net.shutdown_in_progress() ) {
return true; return true;
@@ -327,7 +335,7 @@ bool STMClient::process_user_input( int fd )
} }
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
char the_byte = buf[ i ]; char the_byte = buf[i];
if ( !paste ) { if ( !paste ) {
overlays.get_prediction_engine().new_user_byte( the_byte, local_framebuffer ); overlays.get_prediction_engine().new_user_byte( the_byte, local_framebuffer );
@@ -335,8 +343,9 @@ bool STMClient::process_user_input( int fd )
if ( quit_sequence_started ) { if ( quit_sequence_started ) {
if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */
if ( net.has_remote_addr() && (!net.shutdown_in_progress()) ) { if ( net.has_remote_addr() && ( !net.shutdown_in_progress() ) ) {
overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting on user request..." ), true ); overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting on user request..." ),
true );
net.start_shutdown(); net.start_shutdown();
return true; return true;
} }
@@ -358,7 +367,7 @@ bool STMClient::process_user_input( int fd )
kill( 0, SIGSTOP ); kill( 0, SIGSTOP );
resume(); resume();
} else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) { } else if ( ( the_byte == escape_pass_key ) || ( the_byte == escape_pass_key2 ) ) {
/* Emulation sequence to type escape_key is escape_key + /* Emulation sequence to type escape_key is escape_key +
escape_pass_key (that is escape key without Ctrl) */ escape_pass_key (that is escape key without Ctrl) */
net.get_current_state().push_back( Parser::UserByte( escape_key ) ); net.get_current_state().push_back( Parser::UserByte( escape_key ) );
@@ -377,14 +386,16 @@ bool STMClient::process_user_input( int fd )
continue; continue;
} }
quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf)); quit_sequence_started
= ( escape_key > 0 ) && ( the_byte == escape_key ) && ( lf_entered || ( !escape_requires_lf ) );
if ( quit_sequence_started ) { if ( quit_sequence_started ) {
lf_entered = false; lf_entered = false;
overlays.get_notification_engine().set_notification_string( escape_key_help, true, false ); overlays.get_notification_engine().set_notification_string( escape_key_help, true, false );
continue; continue;
} }
lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */ lf_entered = ( ( the_byte == 0x0A )
|| ( the_byte == 0x0D ) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */
if ( the_byte == 0x0C ) { /* Ctrl-L */ if ( the_byte == 0x0C ) { /* Ctrl-L */
repaint_requested = true; repaint_requested = true;
@@ -427,14 +438,14 @@ bool STMClient::main( void )
/* Drop unnecessary privileges */ /* Drop unnecessary privileges */
#ifdef HAVE_PLEDGE #ifdef HAVE_PLEDGE
/* OpenBSD pledge() syscall */ /* OpenBSD pledge() syscall */
if ( pledge( "stdio inet tty", NULL )) { if ( pledge( "stdio inet tty", NULL ) ) {
perror( "pledge() failed" ); perror( "pledge() failed" );
exit( 1 ); exit( 1 );
} }
#endif #endif
/* prepare to poll for events */ /* prepare to poll for events */
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
while ( 1 ) { while ( 1 ) {
try { try {
@@ -450,10 +461,8 @@ bool STMClient::main( void )
/* poll for events */ /* poll for events */
/* network->fd() can in theory change over time */ /* network->fd() can in theory change over time */
sel.clear_fds(); sel.clear_fds();
std::vector< int > fd_list( network->fds() ); std::vector<int> fd_list( network->fds() );
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end();
it++ ) {
sel.add_fd( *it ); sel.add_fd( *it );
} }
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
@@ -466,9 +475,7 @@ bool STMClient::main( void )
bool network_ready_to_read = false; bool network_ready_to_read = false;
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end();
it++ ) {
if ( sel.read( *it ) ) { if ( sel.read( *it ) ) {
/* packet received from the network */ /* packet received from the network */
/* we only read one socket each run */ /* we only read one socket each run */
@@ -480,7 +487,8 @@ bool STMClient::main( void )
process_network_input(); process_network_input();
} }
if ( sel.read( STDIN_FILENO ) && !process_user_input( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */ if ( sel.read( STDIN_FILENO )
&& !process_user_input( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */
if ( !network->has_remote_addr() ) { if ( !network->has_remote_addr() ) {
break; break;
} else if ( !network->shutdown_in_progress() ) { } else if ( !network->shutdown_in_progress() ) {
@@ -497,15 +505,13 @@ bool STMClient::main( void )
resume(); resume();
} }
if ( sel.signal( SIGTERM ) if ( sel.signal( SIGTERM ) || sel.signal( SIGINT ) || sel.signal( SIGHUP ) || sel.signal( SIGPIPE ) ) {
|| sel.signal( SIGINT )
|| sel.signal( SIGHUP )
|| sel.signal( SIGPIPE ) ) {
/* shutdown signal */ /* shutdown signal */
if ( !network->has_remote_addr() ) { if ( !network->has_remote_addr() ) {
break; break;
} else if ( !network->shutdown_in_progress() ) { } else if ( !network->shutdown_in_progress() ) {
overlays.get_notification_engine().set_notification_string( std::wstring( L"Signal received, shutting down..." ), true ); overlays.get_notification_engine().set_notification_string(
std::wstring( L"Signal received, shutting down..." ), true );
network->start_shutdown(); network->start_shutdown();
} }
} }
@@ -528,33 +534,32 @@ bool STMClient::main( void )
} }
/* write diagnostic message if can't reach server */ /* write diagnostic message if can't reach server */
if ( still_connecting() if ( still_connecting() && ( !network->shutdown_in_progress() )
&& (!network->shutdown_in_progress()) && ( timestamp() - network->get_latest_remote_state().timestamp > 250 ) ) {
&& (timestamp() - network->get_latest_remote_state().timestamp > 250) ) {
if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) { if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) {
if ( !network->shutdown_in_progress() ) { if ( !network->shutdown_in_progress() ) {
overlays.get_notification_engine().set_notification_string( std::wstring( L"Timed out waiting for server..." ), true ); overlays.get_notification_engine().set_notification_string(
std::wstring( L"Timed out waiting for server..." ), true );
network->start_shutdown(); network->start_shutdown();
} }
} else { } else {
overlays.get_notification_engine().set_notification_string( connecting_notification ); overlays.get_notification_engine().set_notification_string( connecting_notification );
} }
} else if ( (network->get_remote_state_num() != 0) } else if ( ( network->get_remote_state_num() != 0 )
&& (overlays.get_notification_engine().get_notification_string() && ( overlays.get_notification_engine().get_notification_string() == connecting_notification ) ) {
== connecting_notification) ) {
overlays.get_notification_engine().set_notification_string( L"" ); overlays.get_notification_engine().set_notification_string( L"" );
} }
network->tick(); network->tick();
std::string & send_error = network->get_send_error(); std::string& send_error = network->get_send_error();
if ( !send_error.empty() ) { if ( !send_error.empty() ) {
overlays.get_notification_engine().set_network_error( send_error ); overlays.get_notification_engine().set_network_error( send_error );
send_error.clear(); send_error.clear();
} else { } else {
overlays.get_notification_engine().clear_network_error(); overlays.get_notification_engine().clear_network_error();
} }
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
if ( !network->shutdown_in_progress() ) { if ( !network->shutdown_in_progress() ) {
overlays.get_notification_engine().set_network_error( e.what() ); overlays.get_notification_engine().set_network_error( e.what() );
} }
@@ -564,11 +569,11 @@ bool STMClient::main( void )
req.tv_nsec = 200000000; /* 0.2 sec */ req.tv_nsec = 200000000; /* 0.2 sec */
nanosleep( &req, NULL ); nanosleep( &req, NULL );
freeze_timestamp(); freeze_timestamp();
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
if ( e.fatal ) { if ( e.fatal ) {
throw; throw;
} else { } else {
wchar_t tmp[ 128 ]; wchar_t tmp[128];
swprintf( tmp, 128, L"Crypto exception: %s", e.what() ); swprintf( tmp, 128, L"Crypto exception: %s", e.what() );
overlays.get_notification_engine().set_notification_string( std::wstring( tmp ) ); overlays.get_notification_engine().set_notification_string( std::wstring( tmp ) );
} }
@@ -576,4 +581,3 @@ bool STMClient::main( void )
} }
return clean_shutdown; return clean_shutdown;
} }
+20 -25
View File
@@ -33,18 +33,19 @@
#ifndef STM_CLIENT_HPP #ifndef STM_CLIENT_HPP
#define STM_CLIENT_HPP #define STM_CLIENT_HPP
#include <string>
#include <memory> #include <memory>
#include <string>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <termios.h> #include <termios.h>
#include "src/statesync/completeterminal.h"
#include "src/network/networktransport.h"
#include "src/statesync/user.h"
#include "src/frontend/terminaloverlay.h" #include "src/frontend/terminaloverlay.h"
#include "src/network/networktransport.h"
#include "src/statesync/completeterminal.h"
#include "src/statesync/user.h"
class STMClient { class STMClient
{
private: private:
std::string ip; std::string ip;
std::string port; std::string port;
@@ -88,24 +89,18 @@ private:
void resume( void ); /* restore state after SIGCONT */ void resume( void ); /* restore state after SIGCONT */
public: public:
STMClient( const char *s_ip, const char *s_port, const char *s_key, const char *predict_mode, unsigned int s_verbose, const char *predict_overwrite ) STMClient( const char* s_ip,
: ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), const char* s_port,
key( s_key ? s_key : "" ), const char* s_key,
escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ), const char* predict_mode,
escape_requires_lf( false ), escape_key_help( L"?" ), unsigned int s_verbose,
saved_termios(), raw_termios(), const char* predict_overwrite )
window_size(), : ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), key( s_key ? s_key : "" ), escape_key( 0x1E ),
local_framebuffer( 1, 1 ), escape_pass_key( '^' ), escape_pass_key2( '^' ), escape_requires_lf( false ), escape_key_help( L"?" ),
new_state( 1, 1 ), saved_termios(), raw_termios(), window_size(), local_framebuffer( 1, 1 ), new_state( 1, 1 ), overlays(),
overlays(), network(), display( true ), /* use TERM environment var to initialize display */
network(), connecting_notification(), repaint_requested( false ), lf_entered( false ), quit_sequence_started( false ),
display( true ), /* use TERM environment var to initialize display */ clean_shutdown( false ), verbose( s_verbose )
connecting_notification(),
repaint_requested( false ),
lf_entered( false ),
quit_sequence_started( false ),
clean_shutdown( false ),
verbose( s_verbose )
{ {
if ( predict_mode ) { if ( predict_mode ) {
if ( !strcmp( predict_mode, "always" ) ) { if ( !strcmp( predict_mode, "always" ) ) {
@@ -131,8 +126,8 @@ public:
bool main( void ); bool main( void );
/* unused */ /* unused */
STMClient( const STMClient & ); STMClient( const STMClient& );
STMClient & operator=( const STMClient & ); STMClient& operator=( const STMClient& );
}; };
#endif #endif
+109 -149
View File
@@ -40,11 +40,9 @@
using namespace Overlay; using namespace Overlay;
void ConditionalOverlayCell::apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const void ConditionalOverlayCell::apply( Framebuffer& fb, uint64_t confirmed_epoch, int row, bool flag ) const
{ {
if ( (!active) if ( ( !active ) || ( row >= fb.ds.get_height() ) || ( col >= fb.ds.get_width() ) ) {
|| (row >= fb.ds.get_height())
|| (col >= fb.ds.get_width()) ) {
return; return;
} }
@@ -58,33 +56,33 @@ void ConditionalOverlayCell::apply( Framebuffer &fb, uint64_t confirmed_epoch, i
if ( unknown ) { if ( unknown ) {
if ( flag && ( col != fb.ds.get_width() - 1 ) ) { if ( flag && ( col != fb.ds.get_width() - 1 ) ) {
fb.get_mutable_cell( row, col )->get_renditions().set_attribute(Renditions::underlined, true); fb.get_mutable_cell( row, col )->get_renditions().set_attribute( Renditions::underlined, true );
} }
return; return;
} }
if ( *fb.get_cell( row, col ) != replacement ) { if ( *fb.get_cell( row, col ) != replacement ) {
*(fb.get_mutable_cell( row, col )) = replacement; *( fb.get_mutable_cell( row, col ) ) = replacement;
if ( flag ) { if ( flag ) {
fb.get_mutable_cell( row, col )->get_renditions().set_attribute( Renditions::underlined, true ); fb.get_mutable_cell( row, col )->get_renditions().set_attribute( Renditions::underlined, true );
} }
} }
} }
Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row, Validity ConditionalOverlayCell::get_validity( const Framebuffer& fb,
uint64_t early_ack __attribute__((unused)), int row,
uint64_t early_ack __attribute__( ( unused ) ),
uint64_t late_ack ) const uint64_t late_ack ) const
{ {
if ( !active ) { if ( !active ) {
return Inactive; return Inactive;
} }
if ( (row >= fb.ds.get_height()) if ( ( row >= fb.ds.get_height() ) || ( col >= fb.ds.get_width() ) ) {
|| (col >= fb.ds.get_width()) ) {
return IncorrectOrExpired; return IncorrectOrExpired;
} }
const Cell &current = *( fb.get_cell( row, col ) ); const Cell& current = *( fb.get_cell( row, col ) );
/* see if it hasn't been updated yet */ /* see if it hasn't been updated yet */
if ( late_ack < expiration_frame ) { if ( late_ack < expiration_frame ) {
@@ -113,23 +111,21 @@ Validity ConditionalOverlayCell::get_validity( const Framebuffer &fb, int row,
return IncorrectOrExpired; return IncorrectOrExpired;
} }
Validity ConditionalCursorMove::get_validity( const Framebuffer &fb, Validity ConditionalCursorMove::get_validity( const Framebuffer& fb,
uint64_t early_ack __attribute((unused)), uint64_t early_ack __attribute( ( unused ) ),
uint64_t late_ack ) const uint64_t late_ack ) const
{ {
if ( !active ) { if ( !active ) {
return Inactive; return Inactive;
} }
if ( (row >= fb.ds.get_height()) if ( ( row >= fb.ds.get_height() ) || ( col >= fb.ds.get_width() ) ) {
|| (col >= fb.ds.get_width()) ) {
// fprintf( stderr, "Crazy cursor (%d,%d)!\n", row, col ); // fprintf( stderr, "Crazy cursor (%d,%d)!\n", row, col );
return IncorrectOrExpired; return IncorrectOrExpired;
} }
if ( late_ack >= expiration_frame ) { if ( late_ack >= expiration_frame ) {
if ( (fb.ds.get_cursor_col() == col) if ( ( fb.ds.get_cursor_col() == col ) && ( fb.ds.get_cursor_row() == row ) ) {
&& (fb.ds.get_cursor_row() == row) ) {
return Correct; return Correct;
} }
return IncorrectOrExpired; return IncorrectOrExpired;
@@ -138,7 +134,7 @@ Validity ConditionalCursorMove::get_validity( const Framebuffer &fb,
return Pending; return Pending;
} }
void ConditionalCursorMove::apply( Framebuffer &fb, uint64_t confirmed_epoch ) const void ConditionalCursorMove::apply( Framebuffer& fb, uint64_t confirmed_epoch ) const
{ {
if ( !active ) { if ( !active ) {
return; return;
@@ -157,29 +153,24 @@ void ConditionalCursorMove::apply( Framebuffer &fb, uint64_t confirmed_epoch ) c
} }
NotificationEngine::NotificationEngine() NotificationEngine::NotificationEngine()
: last_word_from_server( timestamp() ), : last_word_from_server( timestamp() ), last_acked_state( timestamp() ), escape_key_string(), message(),
last_acked_state( timestamp() ), message_is_network_error( false ), message_expiration( -1 ), show_quit_keystroke( true )
escape_key_string(),
message(),
message_is_network_error( false ),
message_expiration( -1 ),
show_quit_keystroke( true )
{} {}
static std::string human_readable_duration( int num_seconds, const std::string &seconds_abbr ) { static std::string human_readable_duration( int num_seconds, const std::string& seconds_abbr )
char tmp[ 128 ]; {
char tmp[128];
if ( num_seconds < 60 ) { if ( num_seconds < 60 ) {
snprintf( tmp, 128, "%d %s", num_seconds, seconds_abbr.c_str() ); snprintf( tmp, 128, "%d %s", num_seconds, seconds_abbr.c_str() );
} else if ( num_seconds < 3600 ) { } else if ( num_seconds < 3600 ) {
snprintf( tmp, 128, "%d:%02d", num_seconds / 60, num_seconds % 60 ); snprintf( tmp, 128, "%d:%02d", num_seconds / 60, num_seconds % 60 );
} else { } else {
snprintf( tmp, 128, "%d:%02d:%02d", num_seconds / 3600, snprintf( tmp, 128, "%d:%02d:%02d", num_seconds / 3600, ( num_seconds / 60 ) % 60, num_seconds % 60 );
(num_seconds / 60) % 60, num_seconds % 60 );
} }
return tmp; return tmp;
} }
void NotificationEngine::apply( Framebuffer &fb ) const void NotificationEngine::apply( Framebuffer& fb ) const
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -204,55 +195,60 @@ void NotificationEngine::apply( Framebuffer &fb ) const
notification_bar.append( 0x20 ); notification_bar.append( 0x20 );
for ( int i = 0; i < fb.ds.get_width(); i++ ) { for ( int i = 0; i < fb.ds.get_width(); i++ ) {
*(fb.get_mutable_cell( 0, i )) = notification_bar; *( fb.get_mutable_cell( 0, i ) ) = notification_bar;
} }
/* write message */ /* write message */
wchar_t tmp[ 128 ]; wchar_t tmp[128];
/* We want to prefer the "last contact" message if we simply haven't /* We want to prefer the "last contact" message if we simply haven't
heard from the server in a while, but print the "last reply" message heard from the server in a while, but print the "last reply" message
if the problem is uplink-only. */ if the problem is uplink-only. */
double since_heard = (double)(now - last_word_from_server) / 1000.0; double since_heard = (double)( now - last_word_from_server ) / 1000.0;
double since_ack = (double)(now - last_acked_state) / 1000.0; double since_ack = (double)( now - last_acked_state ) / 1000.0;
const char server_message[] = "contact"; const char server_message[] = "contact";
const char reply_message[] = "reply"; const char reply_message[] = "reply";
double time_elapsed = since_heard; double time_elapsed = since_heard;
const char *explanation = server_message; const char* explanation = server_message;
if ( reply_late( now ) && (!server_late( now )) ) { if ( reply_late( now ) && ( !server_late( now ) ) ) {
time_elapsed = since_ack; time_elapsed = since_ack;
explanation = reply_message; explanation = reply_message;
} }
const static char blank[] = ""; const static char blank[] = "";
const char *keystroke_str = show_quit_keystroke ? escape_key_string.c_str() : blank; const char* keystroke_str = show_quit_keystroke ? escape_key_string.c_str() : blank;
if ( message.empty() && (!time_expired) ) { if ( message.empty() && ( !time_expired ) ) {
return; return;
} }
if ( message.empty() && time_expired ) { if ( message.empty() && time_expired ) {
swprintf( tmp, 128, L"mosh: Last %s %s ago.%s", explanation, swprintf( tmp,
human_readable_duration( static_cast<int>( time_elapsed ), 128,
"seconds" ).c_str(), L"mosh: Last %s %s ago.%s",
explanation,
human_readable_duration( static_cast<int>( time_elapsed ), "seconds" ).c_str(),
keystroke_str ); keystroke_str );
} else if ( (!message.empty()) && (!time_expired) ) { } else if ( ( !message.empty() ) && ( !time_expired ) ) {
swprintf( tmp, 128, L"mosh: %ls%s", message.c_str(), keystroke_str ); swprintf( tmp, 128, L"mosh: %ls%s", message.c_str(), keystroke_str );
} else { } else {
swprintf( tmp, 128, L"mosh: %ls (%s without %s.)%s", message.c_str(), swprintf( tmp,
human_readable_duration( static_cast<int>( time_elapsed ), 128,
"s" ).c_str(), L"mosh: %ls (%s without %s.)%s",
explanation, keystroke_str ); message.c_str(),
human_readable_duration( static_cast<int>( time_elapsed ), "s" ).c_str(),
explanation,
keystroke_str );
} }
std::wstring string_to_draw( tmp ); std::wstring string_to_draw( tmp );
int overlay_col = 0; int overlay_col = 0;
Cell *combining_cell = fb.get_mutable_cell( 0, 0 ); Cell* combining_cell = fb.get_mutable_cell( 0, 0 );
/* We unfortunately duplicate the terminal's logic for how to render a Unicode sequence into graphemes */ /* We unfortunately duplicate the terminal's logic for how to render a Unicode sequence into graphemes */
for ( std::wstring::const_iterator i = string_to_draw.begin(); i != string_to_draw.end(); i++ ) { for ( std::wstring::const_iterator i = string_to_draw.begin(); i != string_to_draw.end(); i++ ) {
@@ -262,14 +258,14 @@ void NotificationEngine::apply( Framebuffer &fb ) const
wchar_t ch = *i; wchar_t ch = *i;
int chwidth = ch == L'\0' ? -1 : wcwidth( ch ); int chwidth = ch == L'\0' ? -1 : wcwidth( ch );
Cell *this_cell = 0; Cell* this_cell = 0;
switch ( chwidth ) { switch ( chwidth ) {
case 1: /* normal character */ case 1: /* normal character */
case 2: /* wide character */ case 2: /* wide character */
this_cell = fb.get_mutable_cell( 0, overlay_col ); this_cell = fb.get_mutable_cell( 0, overlay_col );
fb.reset_cell( this_cell ); fb.reset_cell( this_cell );
this_cell->get_renditions().set_attribute(Renditions::bold, true); this_cell->get_renditions().set_attribute( Renditions::bold, true );
this_cell->get_renditions().set_foreground_color( 7 ); this_cell->get_renditions().set_foreground_color( 7 );
this_cell->get_renditions().set_background_color( 4 ); this_cell->get_renditions().set_background_color( 4 );
@@ -330,7 +326,7 @@ int NotificationEngine::wait_time( void ) const
return next_expiry; return next_expiry;
} }
void OverlayManager::apply( Framebuffer &fb ) void OverlayManager::apply( Framebuffer& fb )
{ {
predictions.cull( fb ); predictions.cull( fb );
predictions.apply( fb ); predictions.apply( fb );
@@ -339,65 +335,52 @@ void OverlayManager::apply( Framebuffer &fb )
title.apply( fb ); title.apply( fb );
} }
void TitleEngine::set_prefix( const std::wstring &s ) void TitleEngine::set_prefix( const std::wstring& s )
{ {
prefix = Terminal::Framebuffer::title_type( s.begin(), s.end() ); prefix = Terminal::Framebuffer::title_type( s.begin(), s.end() );
} }
void ConditionalOverlayRow::apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const void ConditionalOverlayRow::apply( Framebuffer& fb, uint64_t confirmed_epoch, bool flag ) const
{ {
for ( overlay_cells_type::const_iterator it = overlay_cells.begin(); for ( overlay_cells_type::const_iterator it = overlay_cells.begin(); it != overlay_cells.end(); it++ ) {
it != overlay_cells.end();
it++ ) {
it->apply( fb, confirmed_epoch, row_num, flag ); it->apply( fb, confirmed_epoch, row_num, flag );
} }
} }
void PredictionEngine::apply( Framebuffer &fb ) const void PredictionEngine::apply( Framebuffer& fb ) const
{ {
if ( (display_preference == Never) || !( srtt_trigger if ( ( display_preference == Never )
|| glitch_trigger || !( srtt_trigger || glitch_trigger || ( display_preference == Always )
|| (display_preference == Always) || ( display_preference == Experimental ) ) ) {
|| (display_preference == Experimental) ) ){
return; return;
} }
for ( cursors_type::const_iterator it = cursors.begin(); for ( cursors_type::const_iterator it = cursors.begin(); it != cursors.end(); it++ ) {
it != cursors.end();
it++ ) {
it->apply( fb, confirmed_epoch ); it->apply( fb, confirmed_epoch );
} }
for ( overlays_type::const_iterator it = overlays.begin(); for ( overlays_type::const_iterator it = overlays.begin(); it != overlays.end(); it++ ) {
it != overlays.end();
it++ ) {
it->apply( fb, confirmed_epoch, flagging ); it->apply( fb, confirmed_epoch, flagging );
} }
} }
void PredictionEngine::kill_epoch( uint64_t epoch, const Framebuffer &fb ) void PredictionEngine::kill_epoch( uint64_t epoch, const Framebuffer& fb )
{ {
for( cursors_type::iterator it = cursors.begin(); it != cursors.end(); ) { for ( cursors_type::iterator it = cursors.begin(); it != cursors.end(); ) {
cursors_type::iterator it_next = it; cursors_type::iterator it_next = it;
it_next++; it_next++;
if ( it->tentative( epoch - 1 )) { if ( it->tentative( epoch - 1 ) ) {
cursors.erase( it ); cursors.erase( it );
} }
it = it_next; it = it_next;
} }
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, cursors.push_back( ConditionalCursorMove(
fb.ds.get_cursor_row(), local_frame_sent + 1, fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), prediction_epoch ) );
fb.ds.get_cursor_col(),
prediction_epoch ) );
cursor().active = true; cursor().active = true;
for ( overlays_type::iterator i = overlays.begin(); for ( overlays_type::iterator i = overlays.begin(); i != overlays.end(); i++ ) {
i != overlays.end(); for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
i++ ) {
for ( overlay_cells_type::iterator j = i->overlay_cells.begin();
j != i->overlay_cells.end();
j++ ) {
if ( j->tentative( epoch - 1 ) ) { if ( j->tentative( epoch - 1 ) ) {
j->reset(); j->reset();
} }
@@ -416,35 +399,30 @@ void PredictionEngine::reset( void )
// fprintf( stderr, "RESETTING\n" ); // fprintf( stderr, "RESETTING\n" );
} }
void PredictionEngine::init_cursor( const Framebuffer &fb ) void PredictionEngine::init_cursor( const Framebuffer& fb )
{ {
if ( cursors.empty() ) { if ( cursors.empty() ) {
/* initialize new cursor prediction */ /* initialize new cursor prediction */
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, cursors.push_back( ConditionalCursorMove(
fb.ds.get_cursor_row(), local_frame_sent + 1, fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), prediction_epoch ) );
fb.ds.get_cursor_col(),
prediction_epoch ) );
cursor().active = true; cursor().active = true;
} else if ( cursor().tentative_until_epoch != prediction_epoch ) { } else if ( cursor().tentative_until_epoch != prediction_epoch ) {
cursors.push_back( ConditionalCursorMove( local_frame_sent + 1, cursors.push_back(
cursor().row, ConditionalCursorMove( local_frame_sent + 1, cursor().row, cursor().col, prediction_epoch ) );
cursor().col,
prediction_epoch ) );
cursor().active = true; cursor().active = true;
} }
} }
void PredictionEngine::cull( const Framebuffer &fb ) void PredictionEngine::cull( const Framebuffer& fb )
{ {
if ( display_preference == Never ) { if ( display_preference == Never ) {
return; return;
} }
if ( (last_height != fb.ds.get_height()) if ( ( last_height != fb.ds.get_height() ) || ( last_width != fb.ds.get_width() ) ) {
|| (last_width != fb.ds.get_width()) ) {
last_height = fb.ds.get_height(); last_height = fb.ds.get_height();
last_width = fb.ds.get_width(); last_width = fb.ds.get_width();
reset(); reset();
@@ -455,9 +433,8 @@ void PredictionEngine::cull( const Framebuffer &fb )
/* control srtt_trigger with hysteresis */ /* control srtt_trigger with hysteresis */
if ( send_interval > SRTT_TRIGGER_HIGH ) { if ( send_interval > SRTT_TRIGGER_HIGH ) {
srtt_trigger = true; srtt_trigger = true;
} else if ( srtt_trigger && } else if ( srtt_trigger && ( send_interval <= SRTT_TRIGGER_LOW ) /* 20 ms is current minimum value */
(send_interval <= SRTT_TRIGGER_LOW) /* 20 ms is current minimum value */ && ( !active() ) ) { /* only turn off when no predictions being shown */
&& (!active()) ) { /* only turn off when no predictions being shown */
srtt_trigger = false; srtt_trigger = false;
} }
@@ -479,17 +456,14 @@ void PredictionEngine::cull( const Framebuffer &fb )
while ( i != overlays.end() ) { while ( i != overlays.end() ) {
overlays_type::iterator inext = i; overlays_type::iterator inext = i;
inext++; inext++;
if ( (i->row_num < 0) || (i->row_num >= fb.ds.get_height()) ) { if ( ( i->row_num < 0 ) || ( i->row_num >= fb.ds.get_height() ) ) {
overlays.erase( i ); overlays.erase( i );
i = inext; i = inext;
continue; continue;
} }
for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); for ( overlay_cells_type::iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
j != i->overlay_cells.end(); switch ( j->get_validity( fb, i->row_num, local_frame_acked, local_frame_late_acked ) ) {
j++ ) {
switch ( j->get_validity( fb, i->row_num,
local_frame_acked, local_frame_late_acked ) ) {
case IncorrectOrExpired: case IncorrectOrExpired:
if ( j->tentative( confirmed_epoch ) ) { if ( j->tentative( confirmed_epoch ) ) {
@@ -548,7 +522,6 @@ void PredictionEngine::cull( const Framebuffer &fb )
j->replacement.debug_contents(), i->row_num, j->col, j->replacement.debug_contents(), i->row_num, j->col,
confirmed_epoch, prediction_epoch ); confirmed_epoch, prediction_epoch );
*/ */
} }
/* When predictions come in quickly, slowly take away the glitch trigger. */ /* When predictions come in quickly, slowly take away the glitch trigger. */
@@ -560,10 +533,8 @@ void PredictionEngine::cull( const Framebuffer &fb )
/* match rest of row to the actual renditions */ /* match rest of row to the actual renditions */
{ {
const Renditions &actual_renditions = fb.get_cell( i->row_num, j->col )->get_renditions(); const Renditions& actual_renditions = fb.get_cell( i->row_num, j->col )->get_renditions();
for ( overlay_cells_type::iterator k = j; for ( overlay_cells_type::iterator k = j; k != i->overlay_cells.end(); k++ ) {
k != i->overlay_cells.end();
k++ ) {
k->replacement.get_renditions() = actual_renditions; k->replacement.get_renditions() = actual_renditions;
} }
} }
@@ -576,10 +547,10 @@ void PredictionEngine::cull( const Framebuffer &fb )
case Pending: case Pending:
/* When a prediction takes a long time to be confirmed, we /* When a prediction takes a long time to be confirmed, we
activate the predictions even if SRTT is low */ activate the predictions even if SRTT is low */
if ( (now - j->prediction_time) >= GLITCH_FLAG_THRESHOLD ) { if ( ( now - j->prediction_time ) >= GLITCH_FLAG_THRESHOLD ) {
glitch_trigger = GLITCH_REPAIR_COUNT * 2; /* display and underline */ glitch_trigger = GLITCH_REPAIR_COUNT * 2; /* display and underline */
} else if ( ((now - j->prediction_time) >= GLITCH_THRESHOLD) } else if ( ( ( now - j->prediction_time ) >= GLITCH_THRESHOLD )
&& (glitch_trigger < GLITCH_REPAIR_COUNT) ) { && ( glitch_trigger < GLITCH_REPAIR_COUNT ) ) {
glitch_trigger = GLITCH_REPAIR_COUNT; /* just display */ glitch_trigger = GLITCH_REPAIR_COUNT; /* just display */
} }
@@ -594,8 +565,7 @@ void PredictionEngine::cull( const Framebuffer &fb )
/* go through cursor predictions */ /* go through cursor predictions */
if ( !cursors.empty() if ( !cursors.empty()
&& cursor().get_validity( fb, && cursor().get_validity( fb, local_frame_acked, local_frame_late_acked ) == IncorrectOrExpired ) {
local_frame_acked, local_frame_late_acked ) == IncorrectOrExpired ) {
/* /*
fprintf( stderr, "Sadly, we're predicting (%d,%d) vs. (%d,%d) [tau: %ld, expiration_time=%ld, now=%ld]\n", fprintf( stderr, "Sadly, we're predicting (%d,%d) vs. (%d,%d) [tau: %ld, expiration_time=%ld, now=%ld]\n",
cursor().row, cursor().col, cursor().row, cursor().col,
@@ -615,8 +585,7 @@ void PredictionEngine::cull( const Framebuffer &fb )
/* NB: switching from list to another STL container could break this code. /* NB: switching from list to another STL container could break this code.
So we don't use the cursors_type typedef. */ So we don't use the cursors_type typedef. */
for ( std::list<ConditionalCursorMove>::iterator it = cursors.begin(); for ( std::list<ConditionalCursorMove>::iterator it = cursors.begin(); it != cursors.end(); ) {
it != cursors.end(); ) {
if ( it->get_validity( fb, local_frame_acked, local_frame_late_acked ) != Pending ) { if ( it->get_validity( fb, local_frame_acked, local_frame_late_acked ) != Pending ) {
it = cursors.erase( it ); it = cursors.erase( it );
} else { } else {
@@ -625,7 +594,7 @@ void PredictionEngine::cull( const Framebuffer &fb )
} }
} }
ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_cols ) ConditionalOverlayRow& PredictionEngine::get_or_make_row( int row_num, int num_cols )
{ {
overlays_type::iterator it; overlays_type::iterator it;
@@ -643,13 +612,13 @@ ConditionalOverlayRow & PredictionEngine::get_or_make_row( int row_num, int num_
r.overlay_cells.reserve( num_cols ); r.overlay_cells.reserve( num_cols );
for ( int i = 0; i < num_cols; i++ ) { for ( int i = 0; i < num_cols; i++ ) {
r.overlay_cells.push_back( ConditionalOverlayCell( 0, i, prediction_epoch ) ); r.overlay_cells.push_back( ConditionalOverlayCell( 0, i, prediction_epoch ) );
assert( r.overlay_cells[ i ].col == i ); assert( r.overlay_cells[i].col == i );
} }
overlays.push_back( r ); overlays.push_back( r );
return overlays.back(); return overlays.back();
} }
void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) void PredictionEngine::new_user_byte( char the_byte, const Framebuffer& fb )
{ {
if ( display_preference == Never ) { if ( display_preference == Never ) {
return; return;
@@ -663,8 +632,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
uint64_t now = timestamp(); uint64_t now = timestamp();
/* translate application-mode cursor control function to ANSI cursor control sequence */ /* translate application-mode cursor control function to ANSI cursor control sequence */
if ( (last_byte == 0x1b) if ( ( last_byte == 0x1b ) && ( the_byte == 'O' ) ) {
&& (the_byte == 'O') ) {
the_byte = '['; the_byte = '[';
} }
last_byte = the_byte; last_byte = the_byte;
@@ -672,9 +640,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
Parser::Actions actions; Parser::Actions actions;
parser.input( the_byte, actions ); parser.input( the_byte, actions );
for ( Parser::Actions::iterator it = actions.begin(); for ( Parser::Actions::iterator it = actions.begin(); it != actions.end(); it++ ) {
it != actions.end();
it++ ) {
Parser::Action& act = **it; Parser::Action& act = **it;
/* /*
@@ -695,14 +661,14 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
if ( ch == 0x7f ) { /* backspace */ if ( ch == 0x7f ) { /* backspace */
// fprintf( stderr, "Backspace.\n" ); // fprintf( stderr, "Backspace.\n" );
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); ConditionalOverlayRow& the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
if ( cursor().col > 0 ) { if ( cursor().col > 0 ) {
cursor().col--; cursor().col--;
cursor().expire( local_frame_sent + 1, now ); cursor().expire( local_frame_sent + 1, now );
if ( predict_overwrite ) { if ( predict_overwrite ) {
ConditionalOverlayCell &cell = the_row.overlay_cells[ cursor().col ]; ConditionalOverlayCell& cell = the_row.overlay_cells[cursor().col];
cell.reset_with_orig(); cell.reset_with_orig();
cell.active = true; cell.active = true;
cell.tentative_until_epoch = prediction_epoch; cell.tentative_until_epoch = prediction_epoch;
@@ -711,10 +677,10 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
cell.original_contents.push_back( orig_cell ); cell.original_contents.push_back( orig_cell );
cell.replacement = orig_cell; cell.replacement = orig_cell;
cell.replacement.clear(); cell.replacement.clear();
cell.replacement.append(' '); cell.replacement.append( ' ' );
} else { } else {
for ( int i = cursor().col; i < fb.ds.get_width(); i++ ) { for ( int i = cursor().col; i < fb.ds.get_width(); i++ ) {
ConditionalOverlayCell &cell = the_row.overlay_cells[ i ]; ConditionalOverlayCell& cell = the_row.overlay_cells[i];
cell.reset_with_orig(); cell.reset_with_orig();
cell.active = true; cell.active = true;
@@ -723,8 +689,8 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) ); cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) );
if ( i + 2 < fb.ds.get_width() ) { if ( i + 2 < fb.ds.get_width() ) {
ConditionalOverlayCell &next_cell = the_row.overlay_cells[ i + 1 ]; ConditionalOverlayCell& next_cell = the_row.overlay_cells[i + 1];
const Cell *next_cell_actual = fb.get_cell( cursor().row, i + 1 ); const Cell* next_cell_actual = fb.get_cell( cursor().row, i + 1 );
if ( next_cell.active ) { if ( next_cell.active ) {
if ( next_cell.unknown ) { if ( next_cell.unknown ) {
@@ -743,7 +709,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
} }
} }
} }
} else if ( (ch < 0x20) || (wcwidth( ch ) != 1) ) { } else if ( ( ch < 0x20 ) || ( wcwidth( ch ) != 1 ) ) {
/* unknown print */ /* unknown print */
become_tentative(); become_tentative();
// fprintf( stderr, "Unknown print 0x%x\n", ch ); // fprintf( stderr, "Unknown print 0x%x\n", ch );
@@ -753,7 +719,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
assert( cursor().row < fb.ds.get_height() ); assert( cursor().row < fb.ds.get_height() );
assert( cursor().col < fb.ds.get_width() ); assert( cursor().col < fb.ds.get_width() );
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); ConditionalOverlayRow& the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
if ( cursor().col + 1 >= fb.ds.get_width() ) { if ( cursor().col + 1 >= fb.ds.get_width() ) {
/* prediction in the last column is tricky */ /* prediction in the last column is tricky */
@@ -764,15 +730,15 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
/* do the insert */ /* do the insert */
int rightmost_column = predict_overwrite ? cursor().col : fb.ds.get_width() - 1; int rightmost_column = predict_overwrite ? cursor().col : fb.ds.get_width() - 1;
for ( int i = rightmost_column; i > cursor().col; i-- ) { for ( int i = rightmost_column; i > cursor().col; i-- ) {
ConditionalOverlayCell &cell = the_row.overlay_cells[ i ]; ConditionalOverlayCell& cell = the_row.overlay_cells[i];
cell.reset_with_orig(); cell.reset_with_orig();
cell.active = true; cell.active = true;
cell.tentative_until_epoch = prediction_epoch; cell.tentative_until_epoch = prediction_epoch;
cell.expire( local_frame_sent + 1, now ); cell.expire( local_frame_sent + 1, now );
cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) ); cell.original_contents.push_back( *fb.get_cell( cursor().row, i ) );
ConditionalOverlayCell &prev_cell = the_row.overlay_cells[ i - 1 ]; ConditionalOverlayCell& prev_cell = the_row.overlay_cells[i - 1];
const Cell *prev_cell_actual = fb.get_cell( cursor().row, i - 1 ); const Cell* prev_cell_actual = fb.get_cell( cursor().row, i - 1 );
if ( i == fb.ds.get_width() - 1 ) { if ( i == fb.ds.get_width() - 1 ) {
cell.unknown = true; cell.unknown = true;
@@ -789,7 +755,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
} }
} }
ConditionalOverlayCell &cell = the_row.overlay_cells[ cursor().col ]; ConditionalOverlayCell& cell = the_row.overlay_cells[cursor().col];
cell.reset_with_orig(); cell.reset_with_orig();
cell.active = true; cell.active = true;
cell.tentative_until_epoch = prediction_epoch; cell.tentative_until_epoch = prediction_epoch;
@@ -798,9 +764,9 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
/* heuristic: match renditions of character to the left */ /* heuristic: match renditions of character to the left */
if ( cursor().col > 0 ) { if ( cursor().col > 0 ) {
ConditionalOverlayCell &prev_cell = the_row.overlay_cells[ cursor().col - 1 ]; ConditionalOverlayCell& prev_cell = the_row.overlay_cells[cursor().col - 1];
const Cell *prev_cell_actual = fb.get_cell( cursor().row, cursor().col - 1 ); const Cell* prev_cell_actual = fb.get_cell( cursor().row, cursor().col - 1 );
if ( prev_cell.active && (!prev_cell.unknown) ) { if ( prev_cell.active && ( !prev_cell.unknown ) ) {
cell.replacement.get_renditions() = prev_cell.replacement.get_renditions(); cell.replacement.get_renditions() = prev_cell.replacement.get_renditions();
} else { } else {
cell.replacement.get_renditions() = prev_cell_actual->get_renditions(); cell.replacement.get_renditions() = prev_cell_actual->get_renditions();
@@ -829,7 +795,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
} }
} }
} else if ( type_act == typeid( Parser::Execute ) ) { } else if ( type_act == typeid( Parser::Execute ) ) {
if ( act.char_present && (act.ch == 0x0d) /* CR */ ) { if ( act.char_present && ( act.ch == 0x0d ) /* CR */ ) {
become_tentative(); become_tentative();
newline_carriage_return( fb ); newline_carriage_return( fb );
} else { } else {
@@ -840,13 +806,13 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
// fprintf( stderr, "Escape sequence\n" ); // fprintf( stderr, "Escape sequence\n" );
become_tentative(); become_tentative();
} else if ( type_act == typeid( Parser::CSI_Dispatch ) ) { } else if ( type_act == typeid( Parser::CSI_Dispatch ) ) {
if ( act.char_present && (act.ch == L'C') ) { /* right arrow */ if ( act.char_present && ( act.ch == L'C' ) ) { /* right arrow */
init_cursor( fb ); init_cursor( fb );
if ( cursor().col < fb.ds.get_width() - 1 ) { if ( cursor().col < fb.ds.get_width() - 1 ) {
cursor().col++; cursor().col++;
cursor().expire( local_frame_sent + 1, now ); cursor().expire( local_frame_sent + 1, now );
} }
} else if ( act.char_present && (act.ch == L'D') ) { /* left arrow */ } else if ( act.char_present && ( act.ch == L'D' ) ) { /* left arrow */
init_cursor( fb ); init_cursor( fb );
if ( cursor().col > 0 ) { if ( cursor().col > 0 ) {
@@ -861,7 +827,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb )
} }
} }
void PredictionEngine::newline_carriage_return( const Framebuffer &fb ) void PredictionEngine::newline_carriage_return( const Framebuffer& fb )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
init_cursor( fb ); init_cursor( fb );
@@ -884,10 +850,8 @@ void PredictionEngine::newline_carriage_return( const Framebuffer &fb )
*/ */
/* make blank prediction for last row */ /* make blank prediction for last row */
ConditionalOverlayRow &the_row = get_or_make_row( cursor().row, fb.ds.get_width() ); ConditionalOverlayRow& the_row = get_or_make_row( cursor().row, fb.ds.get_width() );
for ( overlay_cells_type::iterator j = the_row.overlay_cells.begin(); for ( overlay_cells_type::iterator j = the_row.overlay_cells.begin(); j != the_row.overlay_cells.end(); j++ ) {
j != the_row.overlay_cells.end();
j++ ) {
j->active = true; j->active = true;
j->tentative_until_epoch = prediction_epoch; j->tentative_until_epoch = prediction_epoch;
j->expire( local_frame_sent + 1, now ); j->expire( local_frame_sent + 1, now );
@@ -916,12 +880,8 @@ bool PredictionEngine::active( void ) const
return true; return true;
} }
for ( overlays_type::const_iterator i = overlays.begin(); for ( overlays_type::const_iterator i = overlays.begin(); i != overlays.end(); i++ ) {
i != overlays.end(); for ( overlay_cells_type::const_iterator j = i->overlay_cells.begin(); j != i->overlay_cells.end(); j++ ) {
i++ ) {
for ( overlay_cells_type::const_iterator j = i->overlay_cells.begin();
j != i->overlay_cells.end();
j++ ) {
if ( j->active ) { if ( j->active ) {
return true; return true;
} }
+111 -103
View File
@@ -33,28 +33,30 @@
#ifndef TERMINAL_OVERLAY_HPP #ifndef TERMINAL_OVERLAY_HPP
#define TERMINAL_OVERLAY_HPP #define TERMINAL_OVERLAY_HPP
#include "src/terminal/terminalframebuffer.h"
#include "src/network/network.h" #include "src/network/network.h"
#include "src/network/transportsender.h" #include "src/network/transportsender.h"
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
#include "src/terminal/terminalframebuffer.h"
#include <climits> #include <climits>
#include <vector> #include <vector>
namespace Overlay { namespace Overlay {
using namespace Terminal; using namespace Terminal;
using namespace Network; using namespace Network;
enum Validity { enum Validity
{
Pending, Pending,
Correct, Correct,
CorrectNoCredit, CorrectNoCredit,
IncorrectOrExpired, IncorrectOrExpired,
Inactive Inactive
}; };
class ConditionalOverlay { class ConditionalOverlay
public: {
public:
uint64_t expiration_frame; uint64_t expiration_frame;
int col; int col;
bool active; /* represents a prediction at all */ bool active; /* represents a prediction at all */
@@ -62,55 +64,64 @@ namespace Overlay {
uint64_t prediction_time; /* used to find long-pending predictions */ uint64_t prediction_time; /* used to find long-pending predictions */
ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative ) ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative )
: expiration_frame( s_exp ), col( s_col ), : expiration_frame( s_exp ), col( s_col ), active( false ), tentative_until_epoch( s_tentative ),
active( false ), prediction_time( uint64_t( -1 ) )
tentative_until_epoch( s_tentative ), prediction_time( uint64_t( -1 ) )
{} {}
virtual ~ConditionalOverlay() {} virtual ~ConditionalOverlay() {}
bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; } bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; }
void reset( void ) { expiration_frame = tentative_until_epoch = -1; active = false; } void reset( void )
{
expiration_frame = tentative_until_epoch = -1;
active = false;
}
void expire( uint64_t s_exp, uint64_t now ) void expire( uint64_t s_exp, uint64_t now )
{ {
expiration_frame = s_exp; prediction_time = now; expiration_frame = s_exp;
prediction_time = now;
} }
}; };
class ConditionalCursorMove : public ConditionalOverlay { class ConditionalCursorMove : public ConditionalOverlay
public: {
public:
int row; int row;
void apply( Framebuffer &fb, uint64_t confirmed_epoch ) const; void apply( Framebuffer& fb, uint64_t confirmed_epoch ) const;
Validity get_validity( const Framebuffer &fb, uint64_t early_ack, uint64_t late_ack ) const; Validity get_validity( const Framebuffer& fb, uint64_t early_ack, uint64_t late_ack ) const;
ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative ) ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative )
: ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row ) : ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row )
{} {}
}; };
class ConditionalOverlayCell : public ConditionalOverlay { class ConditionalOverlayCell : public ConditionalOverlay
public: {
public:
Cell replacement; Cell replacement;
bool unknown; bool unknown;
std::vector<Cell> original_contents; /* we don't give credit for correct predictions std::vector<Cell> original_contents; /* we don't give credit for correct predictions
that match the original contents */ that match the original contents */
void apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const; void apply( Framebuffer& fb, uint64_t confirmed_epoch, int row, bool flag ) const;
Validity get_validity( const Framebuffer &fb, int row, uint64_t early_ack, uint64_t late_ack ) const; Validity get_validity( const Framebuffer& fb, int row, uint64_t early_ack, uint64_t late_ack ) const;
ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative ) ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative )
: ConditionalOverlay( s_exp, s_col, s_tentative ), : ConditionalOverlay( s_exp, s_col, s_tentative ), replacement( 0 ), unknown( false ), original_contents()
replacement( 0 ),
unknown( false ),
original_contents()
{} {}
void reset( void ) { unknown = false; original_contents.clear(); ConditionalOverlay::reset(); } void reset( void )
void reset_with_orig( void ) { {
if ( (!active) || unknown ) { unknown = false;
original_contents.clear();
ConditionalOverlay::reset();
}
void reset_with_orig( void )
{
if ( ( !active ) || unknown ) {
reset(); reset();
return; return;
} }
@@ -118,23 +129,25 @@ namespace Overlay {
original_contents.push_back( replacement ); original_contents.push_back( replacement );
ConditionalOverlay::reset(); ConditionalOverlay::reset();
} }
}; };
class ConditionalOverlayRow { class ConditionalOverlayRow
public: {
public:
int row_num; int row_num;
using overlay_cells_type = std::vector<ConditionalOverlayCell>; using overlay_cells_type = std::vector<ConditionalOverlayCell>;
overlay_cells_type overlay_cells; overlay_cells_type overlay_cells;
void apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const; void apply( Framebuffer& fb, uint64_t confirmed_epoch, bool flag ) const;
ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {} ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {}
}; };
/* the various overlays */ /* the various overlays */
class NotificationEngine { class NotificationEngine
private: {
private:
uint64_t last_word_from_server; uint64_t last_word_from_server;
uint64_t last_acked_state; uint64_t last_acked_state;
std::string escape_key_string; std::string escape_key_string;
@@ -143,19 +156,21 @@ namespace Overlay {
uint64_t message_expiration; uint64_t message_expiration;
bool show_quit_keystroke; bool show_quit_keystroke;
bool server_late( uint64_t ts ) const { return (ts - last_word_from_server) > 6500; } bool server_late( uint64_t ts ) const { return ( ts - last_word_from_server ) > 6500; }
bool reply_late( uint64_t ts ) const { return (ts - last_acked_state) > 10000; } bool reply_late( uint64_t ts ) const { return ( ts - last_acked_state ) > 10000; }
bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); } bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); }
public: public:
void adjust_message( void ); void adjust_message( void );
void apply( Framebuffer &fb ) const; void apply( Framebuffer& fb ) const;
const std::wstring &get_notification_string( void ) const { return message; } const std::wstring& get_notification_string( void ) const { return message; }
void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; } void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; }
void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; } void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; }
int wait_time( void ) const; int wait_time( void ) const;
void set_notification_string( const std::wstring &s_message, bool permanent = false, bool s_show_quit_keystroke = true ) void set_notification_string( const std::wstring& s_message,
bool permanent = false,
bool s_show_quit_keystroke = true )
{ {
message = s_message; message = s_message;
if ( permanent ) { if ( permanent ) {
@@ -167,16 +182,16 @@ namespace Overlay {
show_quit_keystroke = s_show_quit_keystroke; show_quit_keystroke = s_show_quit_keystroke;
} }
void set_escape_key_string( const std::string &s_name ) void set_escape_key_string( const std::string& s_name )
{ {
char tmp[ 128 ]; char tmp[128];
snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() ); snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() );
escape_key_string = tmp; escape_key_string = tmp;
} }
void set_network_error( const std::string &s ) void set_network_error( const std::string& s )
{ {
wchar_t tmp[ 128 ]; wchar_t tmp[128];
swprintf( tmp, 128, L"%s", s.c_str() ); swprintf( tmp, 128, L"%s", s.c_str() );
message = tmp; message = tmp;
@@ -192,10 +207,11 @@ namespace Overlay {
} }
NotificationEngine(); NotificationEngine();
}; };
class PredictionEngine { class PredictionEngine
private: {
private:
static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */ static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */
static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */ static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */
@@ -221,56 +237,62 @@ namespace Overlay {
uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked; uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked;
ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols ); ConditionalOverlayRow& get_or_make_row( int row_num, int num_cols );
uint64_t prediction_epoch; uint64_t prediction_epoch;
uint64_t confirmed_epoch; uint64_t confirmed_epoch;
void become_tentative( void ); void become_tentative( void );
void newline_carriage_return( const Framebuffer &fb ); void newline_carriage_return( const Framebuffer& fb );
bool flagging; /* whether we are underlining predictions */ bool flagging; /* whether we are underlining predictions */
bool srtt_trigger; /* show predictions because of slow round trip time */ bool srtt_trigger; /* show predictions because of slow round trip time */
unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */ unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */
uint64_t last_quick_confirmation; uint64_t last_quick_confirmation;
ConditionalCursorMove & cursor( void ) { assert( !cursors.empty() ); return cursors.back(); } ConditionalCursorMove& cursor( void )
{
assert( !cursors.empty() );
return cursors.back();
}
void kill_epoch( uint64_t epoch, const Framebuffer &fb ); void kill_epoch( uint64_t epoch, const Framebuffer& fb );
void init_cursor( const Framebuffer &fb ); void init_cursor( const Framebuffer& fb );
unsigned int send_interval; unsigned int send_interval;
int last_height, last_width; int last_height, last_width;
public: public:
enum DisplayPreference { enum DisplayPreference
{
Always, Always,
Never, Never,
Adaptive, Adaptive,
Experimental Experimental
}; };
private: private:
DisplayPreference display_preference; DisplayPreference display_preference;
bool predict_overwrite; bool predict_overwrite;
bool active( void ) const; bool active( void ) const;
bool timing_tests_necessary( void ) const { bool timing_tests_necessary( void ) const
{
/* Are there any timing-based triggers that haven't fired yet? */ /* Are there any timing-based triggers that haven't fired yet? */
return !( glitch_trigger && flagging ); return !( glitch_trigger && flagging );
} }
public: public:
void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; } void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; }
void set_predict_overwrite( bool overwrite ) { predict_overwrite = overwrite; } void set_predict_overwrite( bool overwrite ) { predict_overwrite = overwrite; }
void apply( Framebuffer &fb ) const; void apply( Framebuffer& fb ) const;
void new_user_byte( char the_byte, const Framebuffer &fb ); void new_user_byte( char the_byte, const Framebuffer& fb );
void cull( const Framebuffer &fb ); void cull( const Framebuffer& fb );
void reset( void ); void reset( void );
@@ -280,61 +302,47 @@ namespace Overlay {
void set_send_interval( unsigned int x ) { send_interval = x; } void set_send_interval( unsigned int x ) { send_interval = x; }
int wait_time( void ) const int wait_time( void ) const { return ( timing_tests_necessary() && active() ) ? 50 : INT_MAX; }
{
return ( timing_tests_necessary() && active() )
? 50
: INT_MAX;
}
PredictionEngine( void ) : last_byte( 0 ), parser(), overlays(), cursors(), PredictionEngine( void )
local_frame_sent( 0 ), local_frame_acked( 0 ), : last_byte( 0 ), parser(), overlays(), cursors(), local_frame_sent( 0 ), local_frame_acked( 0 ),
local_frame_late_acked( 0 ), local_frame_late_acked( 0 ), prediction_epoch( 1 ), confirmed_epoch( 0 ), flagging( false ),
prediction_epoch( 1 ), confirmed_epoch( 0 ), srtt_trigger( false ), glitch_trigger( 0 ), last_quick_confirmation( 0 ), send_interval( 250 ),
flagging( false ), last_height( 0 ), last_width( 0 ), display_preference( Adaptive ), predict_overwrite( false )
srtt_trigger( false ), {}
glitch_trigger( 0 ), };
last_quick_confirmation( 0 ),
send_interval( 250 ),
last_height( 0 ), last_width( 0 ),
display_preference( Adaptive ),
predict_overwrite( false )
{
}
};
class TitleEngine { class TitleEngine
private: {
private:
Terminal::Framebuffer::title_type prefix; Terminal::Framebuffer::title_type prefix;
public: public:
void apply( Framebuffer &fb ) const { fb.prefix_window_title( prefix ); } void apply( Framebuffer& fb ) const { fb.prefix_window_title( prefix ); }
TitleEngine() : prefix() {} TitleEngine() : prefix() {}
void set_prefix( const std::wstring &s ); void set_prefix( const std::wstring& s );
}; };
/* the overlay manager */ /* the overlay manager */
class OverlayManager { class OverlayManager
private: {
private:
NotificationEngine notifications; NotificationEngine notifications;
PredictionEngine predictions; PredictionEngine predictions;
TitleEngine title; TitleEngine title;
public: public:
void apply( Framebuffer &fb ); void apply( Framebuffer& fb );
NotificationEngine & get_notification_engine( void ) { return notifications; } NotificationEngine& get_notification_engine( void ) { return notifications; }
PredictionEngine & get_prediction_engine( void ) { return predictions; } PredictionEngine& get_prediction_engine( void ) { return predictions; }
void set_title_prefix( const std::wstring &s ) { title.set_prefix( s ); } void set_title_prefix( const std::wstring& s ) { title.set_prefix( s ); }
OverlayManager() : notifications(), predictions(), title() {} OverlayManager() : notifications(), predictions(), title() {}
int wait_time( void ) const int wait_time( void ) const { return std::min( notifications.wait_time(), predictions.wait_time() ); }
{ };
return std::min( notifications.wait_time(), predictions.wait_time() );
}
};
} }
#endif #endif
+9 -8
View File
@@ -1,17 +1,18 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "src/terminal/parser.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/terminal/parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, size_t size )
Terminal::Display display(false); {
Terminal::Complete complete(80, 24); Terminal::Display display( false );
Terminal::Framebuffer state(80, 24); Terminal::Complete complete( 80, 24 );
for (size_t i = 0; i < size; i++) { Terminal::Framebuffer state( 80, 24 );
complete.act(Parser::UserByte(data[i])); for ( size_t i = 0; i < size; i++ ) {
complete.act( Parser::UserByte( data[i] ) );
} }
display.new_frame(true, state, complete.get_fb()); display.new_frame( true, state, complete.get_fb() );
return 0; return 0;
} }
+4 -3
View File
@@ -3,12 +3,13 @@
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, size_t size )
{
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Parser::Actions result; Parser::Actions result;
for (size_t i = 0; i < size; i++) { for ( size_t i = 0; i < size; i++ ) {
parser.input(data[i], result); parser.input( data[i], result );
} }
return 0; return 0;
+9 -11
View File
@@ -37,26 +37,24 @@
using namespace Network; using namespace Network;
std::string Compressor::compress_str( const std::string &input ) std::string Compressor::compress_str( const std::string& input )
{ {
long unsigned int len = BUFFER_SIZE; long unsigned int len = BUFFER_SIZE;
dos_assert( Z_OK == compress( buffer, &len, dos_assert( Z_OK
reinterpret_cast<const unsigned char *>( input.data() ), == compress( buffer, &len, reinterpret_cast<const unsigned char*>( input.data() ), input.size() ) );
input.size() ) ); return std::string( reinterpret_cast<char*>( buffer ), len );
return std::string( reinterpret_cast<char *>( buffer ), len );
} }
std::string Compressor::uncompress_str( const std::string &input ) std::string Compressor::uncompress_str( const std::string& input )
{ {
long unsigned int len = BUFFER_SIZE; long unsigned int len = BUFFER_SIZE;
dos_assert( Z_OK == uncompress( buffer, &len, dos_assert( Z_OK
reinterpret_cast<const unsigned char *>( input.data() ), == uncompress( buffer, &len, reinterpret_cast<const unsigned char*>( input.data() ), input.size() ) );
input.size() ) ); return std::string( reinterpret_cast<char*>( buffer ), len );
return std::string( reinterpret_cast<char *>( buffer ), len );
} }
/* construct on first use */ /* construct on first use */
Compressor & Network::get_compressor( void ) Compressor& Network::get_compressor( void )
{ {
static Compressor the_compressor; static Compressor the_compressor;
return the_compressor; return the_compressor;
+10 -9
View File
@@ -36,25 +36,26 @@
#include <string> #include <string>
namespace Network { namespace Network {
class Compressor { class Compressor
private: {
private:
static const int BUFFER_SIZE = 2048 * 2048; /* effective limit on terminal size */ static const int BUFFER_SIZE = 2048 * 2048; /* effective limit on terminal size */
unsigned char buffer[BUFFER_SIZE]; unsigned char buffer[BUFFER_SIZE];
public: public:
Compressor() : buffer() {} Compressor() : buffer() {}
~Compressor() {} ~Compressor() {}
std::string compress_str( const std::string &input ); std::string compress_str( const std::string& input );
std::string uncompress_str( const std::string &input ); std::string uncompress_str( const std::string& input );
/* unused */ /* unused */
Compressor( const Compressor & ); Compressor( const Compressor& );
Compressor & operator=( const Compressor & ); Compressor& operator=( const Compressor& );
}; };
Compressor & get_compressor( void ); Compressor& get_compressor( void );
} }
#endif #endif
+111 -145
View File
@@ -36,8 +36,8 @@
#include <cerrno> #include <cerrno>
#include <cstring> #include <cstring>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h>
#ifdef HAVE_SYS_UIO_H #ifdef HAVE_SYS_UIO_H
#include <sys/uio.h> #include <sys/uio.h>
#endif #endif
@@ -45,11 +45,11 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <unistd.h> #include <unistd.h>
#include "src/crypto/byteorder.h"
#include "src/crypto/crypto.h"
#include "src/network/network.h"
#include "src/util/dos_assert.h" #include "src/util/dos_assert.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/crypto/byteorder.h"
#include "src/network/network.h"
#include "src/crypto/crypto.h"
#include "src/util/timestamp.h" #include "src/util/timestamp.h"
@@ -64,22 +64,20 @@
using namespace Network; using namespace Network;
using namespace Crypto; using namespace Crypto;
const uint64_t DIRECTION_MASK = uint64_t(1) << 63; const uint64_t DIRECTION_MASK = uint64_t( 1 ) << 63;
const uint64_t SEQUENCE_MASK = uint64_t(-1) ^ DIRECTION_MASK; const uint64_t SEQUENCE_MASK = uint64_t( -1 ) ^ DIRECTION_MASK;
/* Read in packet */ /* Read in packet */
Packet::Packet( const Message & message ) Packet::Packet( const Message& message )
: seq( message.nonce.val() & SEQUENCE_MASK ), : seq( message.nonce.val() & SEQUENCE_MASK ),
direction( (message.nonce.val() & DIRECTION_MASK) ? TO_CLIENT : TO_SERVER ), direction( ( message.nonce.val() & DIRECTION_MASK ) ? TO_CLIENT : TO_SERVER ), timestamp( -1 ),
timestamp( -1 ), timestamp_reply( -1 ), payload()
timestamp_reply( -1 ),
payload()
{ {
dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) ); dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) );
const uint16_t *data = (uint16_t *)message.text.data(); const uint16_t* data = (uint16_t*)message.text.data();
timestamp = be16toh( data[ 0 ] ); timestamp = be16toh( data[0] );
timestamp_reply = be16toh( data[ 1 ] ); timestamp_reply = be16toh( data[1] );
payload = std::string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() ); payload = std::string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() );
} }
@@ -87,17 +85,17 @@ Packet::Packet( const Message & message )
/* Output from packet */ /* Output from packet */
Message Packet::toMessage( void ) Message Packet::toMessage( void )
{ {
uint64_t direction_seq = (uint64_t( direction == TO_CLIENT ) << 63) | (seq & SEQUENCE_MASK); uint64_t direction_seq = ( uint64_t( direction == TO_CLIENT ) << 63 ) | ( seq & SEQUENCE_MASK );
uint16_t ts_net[ 2 ] = { static_cast<uint16_t>( htobe16( timestamp ) ), uint16_t ts_net[2]
static_cast<uint16_t>( htobe16( timestamp_reply ) ) }; = { static_cast<uint16_t>( htobe16( timestamp ) ), static_cast<uint16_t>( htobe16( timestamp_reply ) ) };
std::string timestamps = std::string( (char *)ts_net, 2 * sizeof( uint16_t ) ); std::string timestamps = std::string( (char*)ts_net, 2 * sizeof( uint16_t ) );
return Message( Nonce( direction_seq ), timestamps + payload ); return Message( Nonce( direction_seq ), timestamps + payload );
} }
Packet Connection::new_packet( const std::string &s_payload ) Packet Connection::new_packet( const std::string& s_payload )
{ {
uint16_t outgoing_timestamp_reply = -1; uint16_t outgoing_timestamp_reply = -1;
@@ -105,7 +103,7 @@ Packet Connection::new_packet( const std::string &s_payload )
if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */ if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */
/* send "corrected" timestamp advanced by how long we held it */ /* send "corrected" timestamp advanced by how long we held it */
outgoing_timestamp_reply = saved_timestamp + (now - saved_timestamp_received_at); outgoing_timestamp_reply = saved_timestamp + ( now - saved_timestamp_received_at );
saved_timestamp = -1; saved_timestamp = -1;
saved_timestamp_received_at = 0; saved_timestamp_received_at = 0;
} }
@@ -149,8 +147,7 @@ void Connection::prune_sockets( void )
} }
} }
Connection::Socket::Socket( int family ) Connection::Socket::Socket( int family ) : _fd( socket( family, SOCK_DGRAM, 0 ) )
: _fd( socket( family, SOCK_DGRAM, 0 ) )
{ {
if ( _fd < 0 ) { if ( _fd < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
@@ -185,13 +182,11 @@ void Connection::setup( void )
last_port_choice = timestamp(); last_port_choice = timestamp();
} }
const std::vector< int > Connection::fds( void ) const const std::vector<int> Connection::fds( void ) const
{ {
std::vector< int > ret; std::vector<int> ret;
for ( std::deque< Socket >::const_iterator it = socks.begin(); for ( std::deque<Socket>::const_iterator it = socks.begin(); it != socks.end(); it++ ) {
it != socks.end();
it++ ) {
ret.push_back( it->fd() ); ret.push_back( it->fd() );
} }
@@ -212,43 +207,31 @@ void Connection::set_MTU( int family )
} }
} }
class AddrInfo { class AddrInfo
{
public: public:
struct addrinfo *res; struct addrinfo* res;
AddrInfo( const char *node, const char *service, AddrInfo( const char* node, const char* service, const struct addrinfo* hints ) : res( NULL )
const struct addrinfo *hints ) : {
res( NULL ) {
int errcode = getaddrinfo( node, service, hints, &res ); int errcode = getaddrinfo( node, service, hints, &res );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "Bad IP address (" ) + (node != NULL ? node : "(null)") + "): " + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "Bad IP address (" ) + ( node != NULL ? node : "(null)" )
+ "): " + gai_strerror( errcode ),
0 );
} }
} }
~AddrInfo() { freeaddrinfo(res); } ~AddrInfo() { freeaddrinfo( res ); }
private: private:
AddrInfo(const AddrInfo &); AddrInfo( const AddrInfo& );
AddrInfo &operator=(const AddrInfo &); AddrInfo& operator=( const AddrInfo& );
}; };
Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */ Connection::Connection( const char* desired_ip, const char* desired_port ) /* server */
: socks(), : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( true ), MTU( DEFAULT_SEND_MTU ),
has_remote_addr( false ), key(), session( key ), direction( TO_CLIENT ), saved_timestamp( -1 ), saved_timestamp_received_at( 0 ),
remote_addr(), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ), last_roundtrip_success( -1 ),
remote_addr_len( 0 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), send_error()
server( true ),
MTU( DEFAULT_SEND_MTU ),
key(),
session( key ),
direction( TO_CLIENT ),
saved_timestamp( -1 ),
saved_timestamp_received_at( 0 ),
expected_receiver_seq( 0 ),
last_heard( -1 ),
last_port_choice( -1 ),
last_roundtrip_success( -1 ),
RTT_hit( false ),
SRTT( 1000 ),
RTTVAR( 500 ),
send_error()
{ {
setup(); setup();
@@ -263,33 +246,34 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
int desired_port_high = -1; int desired_port_high = -1;
if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) { if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) {
throw NetworkException("Invalid port range", 0); throw NetworkException( "Invalid port range", 0 );
} }
/* try to bind to desired IP first */ /* try to bind to desired IP first */
if ( desired_ip ) { if ( desired_ip ) {
try { try {
if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) { return; } if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) {
} catch ( const NetworkException &e ) { return;
fprintf( stderr, "Error binding to IP %s: %s\n", }
desired_ip, } catch ( const NetworkException& e ) {
e.what() ); fprintf( stderr, "Error binding to IP %s: %s\n", desired_ip, e.what() );
} }
} }
/* now try any local interface */ /* now try any local interface */
try { try {
if ( try_bind( NULL, desired_port_low, desired_port_high ) ) { return; } if ( try_bind( NULL, desired_port_low, desired_port_high ) ) {
} catch ( const NetworkException &e ) { return;
fprintf( stderr, "Error binding to any interface: %s\n", }
e.what() ); } catch ( const NetworkException& e ) {
fprintf( stderr, "Error binding to any interface: %s\n", e.what() );
throw; /* this time it's fatal */ throw; /* this time it's fatal */
} }
throw NetworkException( "Could not bind", errno ); throw NetworkException( "Could not bind", errno );
} }
bool Connection::try_bind( const char *addr, int port_low, int port_high ) bool Connection::try_bind( const char* addr, int port_low, int port_high )
{ {
struct addrinfo hints; struct addrinfo hints;
memset( &hints, 0, sizeof( hints ) ); memset( &hints, 0, sizeof( hints ) );
@@ -313,7 +297,7 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
socks.push_back( Socket( local_addr.sa.sa_family ) ); socks.push_back( Socket( local_addr.sa.sa_family ) );
for ( int i = search_low; i <= search_high; i++ ) { for ( int i = search_low; i <= search_high; i++ ) {
switch (local_addr.sa.sa_family) { switch ( local_addr.sa.sa_family ) {
case AF_INET: case AF_INET:
local_addr.sin.sin_port = htons( i ); local_addr.sin.sin_port = htons( i );
break; break;
@@ -325,9 +309,9 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
} }
if ( local_addr.sa.sa_family == AF_INET6 if ( local_addr.sa.sa_family == AF_INET6
&& memcmp(&local_addr.sin6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 ) { && memcmp( &local_addr.sin6.sin6_addr, &in6addr_any, sizeof( in6addr_any ) ) == 0 ) {
const int off = 0; const int off = 0;
if ( setsockopt( sock(), IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off) ) ) { if ( setsockopt( sock(), IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof( off ) ) ) {
perror( "setsockopt( IPV6_V6ONLY, off )" ); perror( "setsockopt( IPV6_V6ONLY, off )" );
} }
} }
@@ -339,38 +323,26 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
} }
int saved_errno = errno; int saved_errno = errno;
socks.pop_back(); socks.pop_back();
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; char host[NI_MAXHOST], serv[NI_MAXSERV];
int errcode = getnameinfo( &local_addr.sa, local_addr_len, int errcode = getnameinfo( &local_addr.sa,
host, sizeof( host ), serv, sizeof( serv ), local_addr_len,
host,
sizeof( host ),
serv,
sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
fprintf( stderr, "Failed binding to %s:%s\n", fprintf( stderr, "Failed binding to %s:%s\n", host, serv );
host, serv );
throw NetworkException( "bind", saved_errno ); throw NetworkException( "bind", saved_errno );
} }
Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */ Connection::Connection( const char* key_str, const char* ip, const char* port ) /* client */
: socks(), : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( false ),
has_remote_addr( false ), MTU( DEFAULT_SEND_MTU ), key( key_str ), session( key ), direction( TO_SERVER ), saved_timestamp( -1 ),
remote_addr(), saved_timestamp_received_at( 0 ), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ),
remote_addr_len( 0 ), last_roundtrip_success( -1 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), send_error()
server( false ),
MTU( DEFAULT_SEND_MTU ),
key( key_str ),
session( key ),
direction( TO_SERVER ),
saved_timestamp( -1 ),
saved_timestamp_received_at( 0 ),
expected_receiver_seq( 0 ),
last_heard( -1 ),
last_port_choice( -1 ),
last_roundtrip_success( -1 ),
RTT_hit( false ),
SRTT( 1000 ),
RTTVAR( 500 ),
send_error()
{ {
setup(); setup();
@@ -392,7 +364,7 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
set_MTU( remote_addr.sa.sa_family ); set_MTU( remote_addr.sa.sa_family );
} }
void Connection::send( const std::string & s ) void Connection::send( const std::string& s )
{ {
if ( !has_remote_addr ) { if ( !has_remote_addr ) {
return; return;
@@ -402,8 +374,7 @@ void Connection::send( const std::string & s )
std::string p = session.encrypt( px.toMessage() ); std::string p = session.encrypt( px.toMessage() );
ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT, ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT, &remote_addr.sa, remote_addr_len );
&remote_addr.sa, remote_addr_len );
if ( bytes_sent != static_cast<ssize_t>( p.size() ) ) { if ( bytes_sent != static_cast<ssize_t>( p.size() ) ) {
/* Make sendto() failure available to the frontend. */ /* Make sendto() failure available to the frontend. */
@@ -422,8 +393,7 @@ void Connection::send( const std::string & s )
fprintf( stderr, "Server now detached from client.\n" ); fprintf( stderr, "Server now detached from client.\n" );
} }
} else { /* client */ } else { /* client */
if ( ( now - last_port_choice > PORT_HOP_INTERVAL ) if ( ( now - last_port_choice > PORT_HOP_INTERVAL ) && ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) {
&& ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) {
hop_port(); hop_port();
} }
} }
@@ -432,15 +402,12 @@ void Connection::send( const std::string & s )
std::string Connection::recv( void ) std::string Connection::recv( void )
{ {
assert( !socks.empty() ); assert( !socks.empty() );
for ( std::deque< Socket >::const_iterator it = socks.begin(); for ( std::deque<Socket>::const_iterator it = socks.begin(); it != socks.end(); it++ ) {
it != socks.end();
it++ ) {
std::string payload; std::string payload;
try { try {
payload = recv_one( it->fd()); payload = recv_one( it->fd() );
} catch ( NetworkException & e ) { } catch ( NetworkException& e ) {
if ( (e.the_errno == EAGAIN) if ( ( e.the_errno == EAGAIN ) || ( e.the_errno == EWOULDBLOCK ) ) {
|| (e.the_errno == EWOULDBLOCK) ) {
continue; continue;
} else { } else {
throw; throw;
@@ -461,8 +428,8 @@ std::string Connection::recv_one( int sock_to_recv )
struct msghdr header; struct msghdr header;
struct iovec msg_iovec; struct iovec msg_iovec;
char msg_payload[ Session::RECEIVE_MTU ]; char msg_payload[Session::RECEIVE_MTU];
char msg_control[ Session::RECEIVE_MTU ]; char msg_control[Session::RECEIVE_MTU];
/* receive source address */ /* receive source address */
header.msg_name = &packet_remote_addr; header.msg_name = &packet_remote_addr;
@@ -494,32 +461,32 @@ std::string Connection::recv_one( int sock_to_recv )
/* receive ECN */ /* receive ECN */
bool congestion_experienced = false; bool congestion_experienced = false;
struct cmsghdr *ecn_hdr = CMSG_FIRSTHDR( &header ); struct cmsghdr* ecn_hdr = CMSG_FIRSTHDR( &header );
if ( ecn_hdr if ( ecn_hdr && ecn_hdr->cmsg_level == IPPROTO_IP
&& ecn_hdr->cmsg_level == IPPROTO_IP
&& ( ecn_hdr->cmsg_type == IP_TOS && ( ecn_hdr->cmsg_type == IP_TOS
#ifdef IP_RECVTOS #ifdef IP_RECVTOS
|| ecn_hdr->cmsg_type == IP_RECVTOS || ecn_hdr->cmsg_type == IP_RECVTOS
#endif #endif
) ) { ) ) {
/* got one */ /* got one */
uint8_t *ecn_octet_p = (uint8_t *)CMSG_DATA( ecn_hdr ); uint8_t* ecn_octet_p = (uint8_t*)CMSG_DATA( ecn_hdr );
assert( ecn_octet_p ); assert( ecn_octet_p );
congestion_experienced = (*ecn_octet_p & 0x03) == 0x03; congestion_experienced = ( *ecn_octet_p & 0x03 ) == 0x03;
} }
Packet p( session.decrypt( msg_payload, received_len ) ); Packet p( session.decrypt( msg_payload, received_len ) );
dos_assert( p.direction == (server ? TO_SERVER : TO_CLIENT) ); /* prevent malicious playback to sender */ dos_assert( p.direction == ( server ? TO_SERVER : TO_CLIENT ) ); /* prevent malicious playback to sender */
if ( p.seq < expected_receiver_seq ) { /* don't use (but do return) out-of-order packets for timestamp or targeting */ if ( p.seq
< expected_receiver_seq ) { /* don't use (but do return) out-of-order packets for timestamp or targeting */
return p.payload; return p.payload;
} }
expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise
screw up the timestamp and targeting */ screw up the timestamp and targeting */
if ( p.timestamp != uint16_t(-1) ) { if ( p.timestamp != uint16_t( -1 ) ) {
saved_timestamp = p.timestamp; saved_timestamp = p.timestamp;
saved_timestamp_received_at = timestamp(); saved_timestamp_received_at = timestamp();
@@ -533,7 +500,7 @@ std::string Connection::recv_one( int sock_to_recv )
} }
} }
if ( p.timestamp_reply != uint16_t(-1) ) { if ( p.timestamp_reply != uint16_t( -1 ) ) {
uint16_t now = timestamp16(); uint16_t now = timestamp16();
double R = timestamp_diff( now, p.timestamp_reply ); double R = timestamp_diff( now, p.timestamp_reply );
@@ -546,8 +513,8 @@ std::string Connection::recv_one( int sock_to_recv )
const double alpha = 1.0 / 8.0; const double alpha = 1.0 / 8.0;
const double beta = 1.0 / 4.0; const double beta = 1.0 / 4.0;
RTTVAR = (1 - beta) * RTTVAR + ( beta * fabs( SRTT - R ) ); RTTVAR = ( 1 - beta ) * RTTVAR + ( beta * fabs( SRTT - R ) );
SRTT = (1 - alpha) * SRTT + ( alpha * R ); SRTT = ( 1 - alpha ) * SRTT + ( alpha * R );
} }
} }
} }
@@ -557,19 +524,22 @@ std::string Connection::recv_one( int sock_to_recv )
last_heard = timestamp(); last_heard = timestamp();
if ( server && /* only client can roam */ if ( server && /* only client can roam */
( remote_addr_len != header.msg_namelen || ( remote_addr_len != header.msg_namelen
memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) ) { || memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) ) {
remote_addr = packet_remote_addr; remote_addr = packet_remote_addr;
remote_addr_len = header.msg_namelen; remote_addr_len = header.msg_namelen;
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; char host[NI_MAXHOST], serv[NI_MAXSERV];
int errcode = getnameinfo( &remote_addr.sa, remote_addr_len, int errcode = getnameinfo( &remote_addr.sa,
host, sizeof( host ), serv, sizeof( serv ), remote_addr_len,
host,
sizeof( host ),
serv,
sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
fprintf( stderr, "Server now attached to client at %s:%s\n", fprintf( stderr, "Server now attached to client at %s:%s\n", host, serv );
host, serv );
} }
return p.payload; return p.payload;
} }
@@ -583,10 +553,8 @@ std::string Connection::port( void ) const
throw NetworkException( "getsockname", errno ); throw NetworkException( "getsockname", errno );
} }
char serv[ NI_MAXSERV ]; char serv[NI_MAXSERV];
int errcode = getnameinfo( &local_addr.sa, addrlen, int errcode = getnameinfo( &local_addr.sa, addrlen, NULL, 0, serv, sizeof( serv ), NI_DGRAM | NI_NUMERICSERV );
NULL, 0, serv, sizeof( serv ),
NI_DGRAM | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
@@ -602,7 +570,7 @@ uint64_t Network::timestamp( void )
uint16_t Network::timestamp16( void ) uint16_t Network::timestamp16( void )
{ {
uint16_t ts = timestamp() % 65536; uint16_t ts = timestamp() % 65536;
if ( ts == uint16_t(-1) ) { if ( ts == uint16_t( -1 ) ) {
ts++; ts++;
} }
return ts; return ts;
@@ -634,18 +602,17 @@ uint64_t Connection::timeout( void ) const
Connection::Socket::~Socket() Connection::Socket::~Socket()
{ {
fatal_assert ( close( _fd ) == 0 ); fatal_assert( close( _fd ) == 0 );
} }
Connection::Socket::Socket( const Socket & other ) Connection::Socket::Socket( const Socket& other ) : _fd( dup( other._fd ) )
: _fd( dup( other._fd ) )
{ {
if ( _fd < 0 ) { if ( _fd < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
} }
} }
Connection::Socket & Connection::Socket::operator=( const Socket & other ) Connection::Socket& Connection::Socket::operator=( const Socket& other )
{ {
if ( dup2( other._fd, _fd ) < 0 ) { if ( dup2( other._fd, _fd ) < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
@@ -654,39 +621,39 @@ Connection::Socket & Connection::Socket::operator=( const Socket & other )
return *this; return *this;
} }
bool Connection::parse_portrange( const char * desired_port, int & desired_port_low, int & desired_port_high ) bool Connection::parse_portrange( const char* desired_port, int& desired_port_low, int& desired_port_high )
{ {
/* parse "port" or "portlow:porthigh" */ /* parse "port" or "portlow:porthigh" */
desired_port_low = desired_port_high = 0; desired_port_low = desired_port_high = 0;
char *end; char* end;
long value; long value;
/* parse first (only?) port */ /* parse first (only?) port */
errno = 0; errno = 0;
value = strtol( desired_port, &end, 10 ); value = strtol( desired_port, &end, 10 );
if ( (errno != 0) || (*end != '\0' && *end != ':') ) { if ( ( errno != 0 ) || ( *end != '\0' && *end != ':' ) ) {
fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port ); fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port );
return false; return false;
} }
if ( (value < 0) || (value > 65535) ) { if ( ( value < 0 ) || ( value > 65535 ) ) {
fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value ); fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value );
return false; return false;
} }
desired_port_low = (int)value; desired_port_low = (int)value;
if (*end == '\0') { /* not a port range */ if ( *end == '\0' ) { /* not a port range */
desired_port_high = desired_port_low; desired_port_high = desired_port_low;
return true; return true;
} }
/* port range; parse high port */ /* port range; parse high port */
const char * cp = end + 1; const char* cp = end + 1;
errno = 0; errno = 0;
value = strtol( cp, &end, 10 ); value = strtol( cp, &end, 10 );
if ( (errno != 0) || (*end != '\0') ) { if ( ( errno != 0 ) || ( *end != '\0' ) ) {
fprintf( stderr, "Invalid high port number (%s)\n", cp ); fprintf( stderr, "Invalid high port number (%s)\n", cp );
return false; return false;
} }
if ( (value < 0) || (value > 65535) ) { if ( ( value < 0 ) || ( value > 65535 ) ) {
fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value ); fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value );
return false; return false;
} }
@@ -702,6 +669,5 @@ bool Connection::parse_portrange( const char * desired_port, int & desired_port_
return false; return false;
} }
return true; return true;
} }
+54 -48
View File
@@ -42,66 +42,71 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h>
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
using namespace Crypto; using namespace Crypto;
namespace Network { namespace Network {
static const unsigned int MOSH_PROTOCOL_VERSION = 2; /* bumped for echo-ack */ static const unsigned int MOSH_PROTOCOL_VERSION = 2; /* bumped for echo-ack */
uint64_t timestamp( void ); uint64_t timestamp( void );
uint16_t timestamp16( void ); uint16_t timestamp16( void );
uint16_t timestamp_diff( uint16_t tsnew, uint16_t tsold ); uint16_t timestamp_diff( uint16_t tsnew, uint16_t tsold );
class NetworkException : public std::exception { class NetworkException : public std::exception
public: {
public:
std::string function; std::string function;
int the_errno; int the_errno;
private:
std::string my_what;
public:
NetworkException( std::string s_function="<none>", int s_errno=0)
: function( s_function ), the_errno( s_errno ),
my_what(function + ": " + strerror(the_errno)) {}
const char *what() const throw () { return my_what.c_str(); }
~NetworkException() throw () {}
};
enum Direction { private:
std::string my_what;
public:
NetworkException( std::string s_function = "<none>", int s_errno = 0 )
: function( s_function ), the_errno( s_errno ), my_what( function + ": " + strerror( the_errno ) )
{}
const char* what() const throw() { return my_what.c_str(); }
~NetworkException() throw() {}
};
enum Direction
{
TO_SERVER = 0, TO_SERVER = 0,
TO_CLIENT = 1 TO_CLIENT = 1
}; };
class Packet { class Packet
public: {
public:
const uint64_t seq; const uint64_t seq;
Direction direction; Direction direction;
uint16_t timestamp, timestamp_reply; uint16_t timestamp, timestamp_reply;
std::string payload; std::string payload;
Packet( Direction s_direction, Packet( Direction s_direction, uint16_t s_timestamp, uint16_t s_timestamp_reply, const std::string& s_payload )
uint16_t s_timestamp, uint16_t s_timestamp_reply, const std::string & s_payload ) : seq( Crypto::unique() ), direction( s_direction ), timestamp( s_timestamp ),
: seq( Crypto::unique() ), direction( s_direction ), timestamp_reply( s_timestamp_reply ), payload( s_payload )
timestamp( s_timestamp ), timestamp_reply( s_timestamp_reply ), payload( s_payload )
{} {}
Packet( const Message & message ); Packet( const Message& message );
Message toMessage( void ); Message toMessage( void );
}; };
union Addr { union Addr {
struct sockaddr sa; struct sockaddr sa;
struct sockaddr_in sin; struct sockaddr_in sin;
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
struct sockaddr_storage ss; struct sockaddr_storage ss;
}; };
class Connection { class Connection
private: {
private:
/* /*
* For IPv4, guess the typical (minimum) header length; * For IPv4, guess the typical (minimum) header length;
* fragmentation is not dangerous, just inefficient. * fragmentation is not dangerous, just inefficient.
@@ -145,7 +150,7 @@ namespace Network {
static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */ static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */
bool try_bind( const char *addr, int port_low, int port_high ); bool try_bind( const char* addr, int port_low, int port_high );
class Socket class Socket
{ {
@@ -157,11 +162,11 @@ namespace Network {
Socket( int family ); Socket( int family );
~Socket(); ~Socket();
Socket( const Socket & other ); Socket( const Socket& other );
Socket & operator=( const Socket & other ); Socket& operator=( const Socket& other );
}; };
std::deque< Socket > socks; std::deque<Socket> socks;
bool has_remote_addr; bool has_remote_addr;
Addr remote_addr; Addr remote_addr;
socklen_t remote_addr_len; socklen_t remote_addr_len;
@@ -191,11 +196,15 @@ namespace Network {
/* Error from send()/sendto(). */ /* Error from send()/sendto(). */
std::string send_error; std::string send_error;
Packet new_packet( const std::string &s_payload ); Packet new_packet( const std::string& s_payload );
void hop_port( void ); void hop_port( void );
int sock( void ) const { assert( !socks.empty() ); return socks.back().fd(); } int sock( void ) const
{
assert( !socks.empty() );
return socks.back().fd();
}
void prune_sockets( void ); void prune_sockets( void );
@@ -203,16 +212,16 @@ namespace Network {
void set_MTU( int family ); void set_MTU( int family );
public: public:
/* Network transport overhead. */ /* Network transport overhead. */
static const int ADDED_BYTES = 8 /* seqno/nonce */ + 4 /* timestamps */; static const int ADDED_BYTES = 8 /* seqno/nonce */ + 4 /* timestamps */;
Connection( const char *desired_ip, const char *desired_port ); /* server */ Connection( const char* desired_ip, const char* desired_port ); /* server */
Connection( const char *key_str, const char *ip, const char *port ); /* client */ Connection( const char* key_str, const char* ip, const char* port ); /* client */
void send( const std::string & s ); void send( const std::string& s );
std::string recv( void ); std::string recv( void );
const std::vector< int > fds( void ) const; const std::vector<int> fds( void ) const;
int get_MTU( void ) const { return MTU; } int get_MTU( void ) const { return MTU; }
std::string port( void ) const; std::string port( void ) const;
@@ -222,18 +231,15 @@ namespace Network {
uint64_t timeout( void ) const; uint64_t timeout( void ) const;
double get_SRTT( void ) const { return SRTT; } double get_SRTT( void ) const { return SRTT; }
const Addr &get_remote_addr( void ) const { return remote_addr; } const Addr& get_remote_addr( void ) const { return remote_addr; }
socklen_t get_remote_addr_len( void ) const { return remote_addr_len; } socklen_t get_remote_addr_len( void ) const { return remote_addr_len; }
std::string &get_send_error( void ) std::string& get_send_error( void ) { return send_error; }
{
return send_error;
}
void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; } void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; }
static bool parse_portrange( const char * desired_port_range, int & desired_port_low, int & desired_port_high ); static bool parse_portrange( const char* desired_port_range, int& desired_port_low, int& desired_port_high );
}; };
} }
#endif #endif
+43 -35
View File
@@ -39,35 +39,32 @@
using namespace Network; using namespace Network;
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote, Transport<MyState, RemoteState>::Transport( MyState& initial_state,
const char *desired_ip, const char *desired_port ) RemoteState& initial_remote,
: connection( desired_ip, desired_port ), const char* desired_ip,
sender( &connection, initial_state ), const char* desired_port )
: connection( desired_ip, desired_port ), sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ), received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
receiver_quench_timer( 0 ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( 0 )
last_receiver_state( initial_remote ),
fragments(),
verbose( 0 )
{ {
/* server */ /* server */
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote, Transport<MyState, RemoteState>::Transport( MyState& initial_state,
const char *key_str, const char *ip, const char *port ) RemoteState& initial_remote,
: connection( key_str, ip, port ), const char* key_str,
sender( &connection, initial_state ), const char* ip,
const char* port )
: connection( key_str, ip, port ), sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ), received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
receiver_quench_timer( 0 ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( 0 )
last_receiver_state( initial_remote ),
fragments(),
verbose( 0 )
{ {
/* client */ /* client */
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
void Transport<MyState, RemoteState>::recv( void ) void Transport<MyState, RemoteState>::recv( void )
{ {
std::string s( connection.recv() ); std::string s( connection.recv() );
@@ -86,7 +83,7 @@ void Transport<MyState, RemoteState>::recv( void )
connection.set_last_roundtrip_success( sender.get_sent_state_acked_timestamp() ); connection.set_last_roundtrip_success( sender.get_sent_state_acked_timestamp() );
/* first, make sure we don't already have the new state */ /* first, make sure we don't already have the new state */
for ( typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); for ( typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
i != received_states.end(); i != received_states.end();
i++ ) { i++ ) {
if ( inst.new_num() == i->num ) { if ( inst.new_num() == i->num ) {
@@ -96,7 +93,7 @@ void Transport<MyState, RemoteState>::recv( void )
/* now, make sure we do have the old state */ /* now, make sure we do have the old state */
bool found = 0; bool found = 0;
typename std::list< TimestampedState<RemoteState> >::iterator reference_state = received_states.begin(); typename std::list<TimestampedState<RemoteState>>::iterator reference_state = received_states.begin();
while ( reference_state != received_states.end() ) { while ( reference_state != received_states.end() ) {
if ( inst.old_num() == reference_state->num ) { if ( inst.old_num() == reference_state->num ) {
found = true; found = true;
@@ -106,7 +103,8 @@ void Transport<MyState, RemoteState>::recv( void )
} }
if ( !found ) { if ( !found ) {
// fprintf( stderr, "Ignoring out-of-order packet. Reference state %d has been discarded or hasn't yet been received.\n", int(inst.old_num) ); // fprintf( stderr, "Ignoring out-of-order packet. Reference state %d has been discarded or hasn't yet been
// received.\n", int(inst.old_num) );
return; /* this is security-sensitive and part of how we enforce idempotency */ return; /* this is security-sensitive and part of how we enforce idempotency */
} }
@@ -121,8 +119,11 @@ void Transport<MyState, RemoteState>::recv( void )
uint64_t now = timestamp(); uint64_t now = timestamp();
if ( now < receiver_quench_timer ) { /* deny letting state grow further */ if ( now < receiver_quench_timer ) { /* deny letting state grow further */
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Receiver queue full, discarding %d (malicious sender or long-unidirectional connectivity?)\n", fprintf(
(unsigned int)(timestamp() % 100000), (int)inst.new_num() ); stderr,
"[%u] Receiver queue full, discarding %d (malicious sender or long-unidirectional connectivity?)\n",
(unsigned int)( timestamp() % 100000 ),
(int)inst.new_num() );
} }
return; return;
} else { } else {
@@ -140,21 +141,28 @@ void Transport<MyState, RemoteState>::recv( void )
} }
/* Insert new state in sorted place */ /* Insert new state in sorted place */
for ( typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); for ( typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
i != received_states.end(); i != received_states.end();
i++ ) { i++ ) {
if ( i->num > new_state.num ) { if ( i->num > new_state.num ) {
received_states.insert( i, new_state ); received_states.insert( i, new_state );
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Received OUT-OF-ORDER state %d [ack %d]\n", fprintf( stderr,
(unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.ack_num() ); "[%u] Received OUT-OF-ORDER state %d [ack %d]\n",
(unsigned int)( timestamp() % 100000 ),
(int)new_state.num,
(int)inst.ack_num() );
} }
return; return;
} }
} }
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Received state %d [coming from %d, ack %d]\n", fprintf( stderr,
(unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.old_num(), (int)inst.ack_num() ); "[%u] Received state %d [coming from %d, ack %d]\n",
(unsigned int)( timestamp() % 100000 ),
(int)new_state.num,
(int)inst.old_num(),
(int)inst.ack_num() );
} }
received_states.push_back( new_state ); received_states.push_back( new_state );
sender.set_ack_num( received_states.back().num ); sender.set_ack_num( received_states.back().num );
@@ -167,12 +175,12 @@ void Transport<MyState, RemoteState>::recv( void )
} }
/* The sender uses throwaway_num to tell us the earliest received state that we need to keep around */ /* The sender uses throwaway_num to tell us the earliest received state that we need to keep around */
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwaway_num ) void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwaway_num )
{ {
typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
while ( i != received_states.end() ) { while ( i != received_states.end() ) {
typename std::list< TimestampedState<RemoteState> >::iterator inext = i; typename std::list<TimestampedState<RemoteState>>::iterator inext = i;
inext++; inext++;
if ( i->num < throwaway_num ) { if ( i->num < throwaway_num ) {
received_states.erase( i ); received_states.erase( i );
@@ -183,16 +191,16 @@ void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwawa
fatal_assert( received_states.size() > 0 ); fatal_assert( received_states.size() > 0 );
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
std::string Transport<MyState, RemoteState>::get_remote_diff( void ) std::string Transport<MyState, RemoteState>::get_remote_diff( void )
{ {
/* find diff between last receiver state and current remote state, then rationalize states */ /* find diff between last receiver state and current remote state, then rationalize states */
std::string ret( received_states.back().state.diff_from( last_receiver_state ) ); std::string ret( received_states.back().state.diff_from( last_receiver_state ) );
const RemoteState *oldest_receiver_state = &received_states.front().state; const RemoteState* oldest_receiver_state = &received_states.front().state;
for ( typename std::list< TimestampedState<RemoteState> >::reverse_iterator i = received_states.rbegin(); for ( typename std::list<TimestampedState<RemoteState>>::reverse_iterator i = received_states.rbegin();
i != received_states.rend(); i != received_states.rend();
i++ ) { i++ ) {
i->state.subtract( oldest_receiver_state ); i->state.subtract( oldest_receiver_state );
+27 -18
View File
@@ -44,10 +44,10 @@
#include "transportfragment.h" #include "transportfragment.h"
namespace Network { namespace Network {
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
class Transport class Transport
{ {
private: private:
/* the underlying, encrypted network connection */ /* the underlying, encrypted network connection */
Connection connection; Connection connection;
@@ -58,17 +58,22 @@ namespace Network {
void process_throwaway_until( uint64_t throwaway_num ); void process_throwaway_until( uint64_t throwaway_num );
/* simple receiver */ /* simple receiver */
std::list< TimestampedState<RemoteState> > received_states; std::list<TimestampedState<RemoteState>> received_states;
uint64_t receiver_quench_timer; uint64_t receiver_quench_timer;
RemoteState last_receiver_state; /* the state we were in when user last queried state */ RemoteState last_receiver_state; /* the state we were in when user last queried state */
FragmentAssembly fragments; FragmentAssembly fragments;
unsigned int verbose; unsigned int verbose;
public: public:
Transport( MyState &initial_state, RemoteState &initial_remote, Transport( MyState& initial_state,
const char *desired_ip, const char *desired_port ); RemoteState& initial_remote,
Transport( MyState &initial_state, RemoteState &initial_remote, const char* desired_ip,
const char *key_str, const char *ip, const char *port ); const char* desired_port );
Transport( MyState& initial_state,
RemoteState& initial_remote,
const char* key_str,
const char* ip,
const char* port );
/* Send data or an ack if necessary. */ /* Send data or an ack if necessary. */
void tick( void ) { sender.tick(); } void tick( void ) { sender.tick(); }
@@ -96,16 +101,20 @@ namespace Network {
std::string port( void ) const { return connection.port(); } std::string port( void ) const { return connection.port(); }
std::string get_key( void ) const { return connection.get_key(); } std::string get_key( void ) const { return connection.get_key(); }
MyState &get_current_state( void ) { return sender.get_current_state(); } MyState& get_current_state( void ) { return sender.get_current_state(); }
void set_current_state( const MyState &x ) { sender.set_current_state( x ); } void set_current_state( const MyState& x ) { sender.set_current_state( x ); }
uint64_t get_remote_state_num( void ) const { return received_states.back().num; } uint64_t get_remote_state_num( void ) const { return received_states.back().num; }
const TimestampedState<RemoteState> & get_latest_remote_state( void ) const { return received_states.back(); } const TimestampedState<RemoteState>& get_latest_remote_state( void ) const { return received_states.back(); }
const std::vector< int > fds( void ) const { return connection.fds(); } const std::vector<int> fds( void ) const { return connection.fds(); }
void set_verbose( unsigned int s_verbose ) { sender.set_verbose( s_verbose ); verbose = s_verbose; } void set_verbose( unsigned int s_verbose )
{
sender.set_verbose( s_verbose );
verbose = s_verbose;
}
void set_send_delay( int new_delay ) { sender.set_send_delay( new_delay ); } void set_send_delay( int new_delay ) { sender.set_send_delay( new_delay ); }
@@ -115,11 +124,11 @@ namespace Network {
unsigned int send_interval( void ) const { return sender.send_interval(); } unsigned int send_interval( void ) const { return sender.send_interval(); }
const Addr &get_remote_addr( void ) const { return connection.get_remote_addr(); } const Addr& get_remote_addr( void ) const { return connection.get_remote_addr(); }
socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); } socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); }
std::string &get_send_error( void ) { return connection.get_send_error(); } std::string& get_send_error( void ) { return connection.get_send_error(); }
}; };
} }
#endif #endif
+21 -25
View File
@@ -32,11 +32,11 @@
#include <cassert> #include <cassert>
#include "src/crypto/byteorder.h"
#include "transportfragment.h"
#include "src/protobufs/transportinstruction.pb.h"
#include "compressor.h" #include "compressor.h"
#include "src/crypto/byteorder.h"
#include "src/protobufs/transportinstruction.pb.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "transportfragment.h"
using namespace Network; using namespace Network;
using namespace TransportBuffers; using namespace TransportBuffers;
@@ -44,13 +44,13 @@ using namespace TransportBuffers;
static std::string network_order_string( uint16_t host_order ) static std::string network_order_string( uint16_t host_order )
{ {
uint16_t net_int = htobe16( host_order ); uint16_t net_int = htobe16( host_order );
return std::string( (char *)&net_int, sizeof( net_int ) ); return std::string( (char*)&net_int, sizeof( net_int ) );
} }
static std::string network_order_string( uint64_t host_order ) static std::string network_order_string( uint64_t host_order )
{ {
uint64_t net_int = htobe64( host_order ); uint64_t net_int = htobe64( host_order );
return std::string( (char *)&net_int, sizeof( net_int ) ); return std::string( (char*)&net_int, sizeof( net_int ) );
} }
std::string Fragment::tostring( void ) std::string Fragment::tostring( void )
@@ -61,7 +61,8 @@ std::string Fragment::tostring( void )
ret += network_order_string( id ); ret += network_order_string( id );
fatal_assert( !( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */ fatal_assert(
!( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */
uint16_t combined_fragment_num = ( final << 15 ) | fragment_num; uint16_t combined_fragment_num = ( final << 15 ) | fragment_num;
ret += network_order_string( combined_fragment_num ); ret += network_order_string( combined_fragment_num );
@@ -72,23 +73,22 @@ std::string Fragment::tostring( void )
return ret; return ret;
} }
Fragment::Fragment( const std::string &x ) Fragment::Fragment( const std::string& x )
: id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), : id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), contents()
contents()
{ {
fatal_assert( x.size() >= frag_header_len ); fatal_assert( x.size() >= frag_header_len );
contents = std::string( x.begin() + frag_header_len, x.end() ); contents = std::string( x.begin() + frag_header_len, x.end() );
uint64_t data64; uint64_t data64;
uint16_t *data16 = (uint16_t *)x.data(); uint16_t* data16 = (uint16_t*)x.data();
memcpy( &data64, x.data(), sizeof( data64 ) ); memcpy( &data64, x.data(), sizeof( data64 ) );
id = be64toh( data64 ); id = be64toh( data64 );
fragment_num = be16toh( data16[ 4 ] ); fragment_num = be16toh( data16[4] );
final = ( fragment_num & 0x8000 ) >> 15; final = ( fragment_num & 0x8000 ) >> 15;
fragment_num &= 0x7FFF; fragment_num &= 0x7FFF;
} }
bool FragmentAssembly::add_fragment( Fragment &frag ) bool FragmentAssembly::add_fragment( Fragment& frag )
{ {
/* see if this is a totally new packet */ /* see if this is a totally new packet */
if ( current_id != frag.id ) { if ( current_id != frag.id ) {
@@ -100,8 +100,7 @@ bool FragmentAssembly::add_fragment( Fragment &frag )
current_id = frag.id; current_id = frag.id;
} else { /* not a new packet */ } else { /* not a new packet */
/* see if we already have this fragment */ /* see if we already have this fragment */
if ( (fragments.size() > frag.fragment_num) if ( ( fragments.size() > frag.fragment_num ) && ( fragments.at( frag.fragment_num ).initialized ) ) {
&& (fragments.at( frag.fragment_num ).initialized) ) {
/* make sure new version is same as what we already have */ /* make sure new version is same as what we already have */
assert( fragments.at( frag.fragment_num ) == frag ); assert( fragments.at( frag.fragment_num ) == frag );
} else { } else {
@@ -148,27 +147,24 @@ Instruction FragmentAssembly::get_assembly( void )
return ret; return ret;
} }
bool Fragment::operator==( const Fragment &x ) const bool Fragment::operator==( const Fragment& x ) const
{ {
return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final ) return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final )
&& ( initialized == x.initialized ) && ( contents == x.contents ); && ( initialized == x.initialized ) && ( contents == x.contents );
} }
std::vector<Fragment> Fragmenter::make_fragments( const Instruction &inst, size_t MTU ) std::vector<Fragment> Fragmenter::make_fragments( const Instruction& inst, size_t MTU )
{ {
MTU -= Fragment::frag_header_len; MTU -= Fragment::frag_header_len;
if ( (inst.old_num() != last_instruction.old_num()) if ( ( inst.old_num() != last_instruction.old_num() ) || ( inst.new_num() != last_instruction.new_num() )
|| (inst.new_num() != last_instruction.new_num()) || ( inst.ack_num() != last_instruction.ack_num() )
|| (inst.ack_num() != last_instruction.ack_num()) || ( inst.throwaway_num() != last_instruction.throwaway_num() )
|| (inst.throwaway_num() != last_instruction.throwaway_num()) || ( inst.chaff() != last_instruction.chaff() )
|| (inst.chaff() != last_instruction.chaff()) || ( inst.protocol_version() != last_instruction.protocol_version() ) || ( last_MTU != MTU ) ) {
|| (inst.protocol_version() != last_instruction.protocol_version())
|| (last_MTU != MTU) ) {
next_instruction_id++; next_instruction_id++;
} }
if ( (inst.old_num() == last_instruction.old_num()) if ( ( inst.old_num() == last_instruction.old_num() ) && ( inst.new_num() == last_instruction.new_num() ) ) {
&& (inst.new_num() == last_instruction.new_num()) ) {
assert( inst.diff() == last_instruction.diff() ); assert( inst.diff() == last_instruction.diff() );
} }
+23 -26
View File
@@ -40,11 +40,11 @@
#include "src/protobufs/transportinstruction.pb.h" #include "src/protobufs/transportinstruction.pb.h"
namespace Network { namespace Network {
using namespace TransportBuffers; using namespace TransportBuffers;
class Fragment class Fragment
{ {
public: public:
static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t ); static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t );
uint64_t id; uint64_t id;
@@ -55,51 +55,48 @@ namespace Network {
std::string contents; std::string contents;
Fragment() Fragment() : id( -1 ), fragment_num( -1 ), final( false ), initialized( false ), contents() {}
: id( -1 ), fragment_num( -1 ), final( false ), initialized( false ), contents()
Fragment( uint64_t s_id, uint16_t s_fragment_num, bool s_final, const std::string& s_contents )
: id( s_id ), fragment_num( s_fragment_num ), final( s_final ), initialized( true ), contents( s_contents )
{} {}
Fragment( uint64_t s_id, uint16_t s_fragment_num, bool s_final, const std::string & s_contents ) Fragment( const std::string& x );
: id( s_id ), fragment_num( s_fragment_num ), final( s_final ), initialized( true ),
contents( s_contents )
{}
Fragment( const std::string &x );
std::string tostring( void ); std::string tostring( void );
bool operator==( const Fragment &x ) const; bool operator==( const Fragment& x ) const;
}; };
class FragmentAssembly class FragmentAssembly
{ {
private: private:
std::vector<Fragment> fragments; std::vector<Fragment> fragments;
uint64_t current_id; uint64_t current_id;
int fragments_arrived, fragments_total; int fragments_arrived, fragments_total;
public: public:
FragmentAssembly() : fragments(), current_id( -1 ), fragments_arrived( 0 ), fragments_total( -1 ) {} FragmentAssembly() : fragments(), current_id( -1 ), fragments_arrived( 0 ), fragments_total( -1 ) {}
bool add_fragment( Fragment &inst ); bool add_fragment( Fragment& inst );
Instruction get_assembly( void ); Instruction get_assembly( void );
}; };
class Fragmenter class Fragmenter
{ {
private: private:
uint64_t next_instruction_id; uint64_t next_instruction_id;
Instruction last_instruction; Instruction last_instruction;
size_t last_MTU; size_t last_MTU;
public: public:
Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 ) Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 )
{ {
last_instruction.set_old_num( -1 ); last_instruction.set_old_num( -1 );
last_instruction.set_new_num( -1 ); last_instruction.set_new_num( -1 );
} }
std::vector<Fragment> make_fragments( const Instruction &inst, size_t MTU ); std::vector<Fragment> make_fragments( const Instruction& inst, size_t MTU );
uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); } uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); }
}; };
} }
+67 -77
View File
@@ -46,30 +46,18 @@
using namespace Network; using namespace Network;
template <class MyState> template<class MyState>
TransportSender<MyState>::TransportSender( Connection *s_connection, MyState &initial_state ) TransportSender<MyState>::TransportSender( Connection* s_connection, MyState& initial_state )
: connection( s_connection ), : connection( s_connection ), current_state( initial_state ),
current_state( initial_state ),
sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ), sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ),
assumed_receiver_state( sent_states.begin() ), assumed_receiver_state( sent_states.begin() ), fragmenter(), next_ack_time( timestamp() ),
fragmenter(), next_send_time( timestamp() ), verbose( 0 ), shutdown_in_progress( false ), shutdown_tries( 0 ),
next_ack_time( timestamp() ), shutdown_start( -1 ), ack_num( 0 ), pending_data_ack( false ), SEND_MINDELAY( 8 ), last_heard( 0 ), prng(),
next_send_time( timestamp() ),
verbose( 0 ),
shutdown_in_progress( false ),
shutdown_tries( 0 ),
shutdown_start( -1 ),
ack_num( 0 ),
pending_data_ack( false ),
SEND_MINDELAY( 8 ),
last_heard( 0 ),
prng(),
mindelay_clock( -1 ) mindelay_clock( -1 )
{ {}
}
/* Try to send roughly two frames per RTT, bounded by limits on frame rate */ /* Try to send roughly two frames per RTT, bounded by limits on frame rate */
template <class MyState> template<class MyState>
unsigned int TransportSender<MyState>::send_interval( void ) const unsigned int TransportSender<MyState>::send_interval( void ) const
{ {
int SEND_INTERVAL = lrint( ceil( connection->get_SRTT() / 2.0 ) ); int SEND_INTERVAL = lrint( ceil( connection->get_SRTT() / 2.0 ) );
@@ -83,7 +71,7 @@ unsigned int TransportSender<MyState>::send_interval( void ) const
} }
/* Housekeeping routine to calculate next send and ack times */ /* Housekeeping routine to calculate next send and ack times */
template <class MyState> template<class MyState>
void TransportSender<MyState>::calculate_timers( void ) void TransportSender<MyState>::calculate_timers( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -94,38 +82,35 @@ void TransportSender<MyState>::calculate_timers( void )
/* Cut out common prefix of all states */ /* Cut out common prefix of all states */
rationalize_states(); rationalize_states();
if ( pending_data_ack && (next_ack_time > now + ACK_DELAY) ) { if ( pending_data_ack && ( next_ack_time > now + ACK_DELAY ) ) {
next_ack_time = now + ACK_DELAY; next_ack_time = now + ACK_DELAY;
} }
if ( !(current_state == sent_states.back().state) ) { if ( !( current_state == sent_states.back().state ) ) {
if ( mindelay_clock == uint64_t( -1 ) ) { if ( mindelay_clock == uint64_t( -1 ) ) {
mindelay_clock = now; mindelay_clock = now;
} }
next_send_time = std::max( mindelay_clock + SEND_MINDELAY, next_send_time = std::max( mindelay_clock + SEND_MINDELAY, sent_states.back().timestamp + send_interval() );
sent_states.back().timestamp + send_interval() ); } else if ( !( current_state == assumed_receiver_state->state ) && ( last_heard + ACTIVE_RETRY_TIMEOUT > now ) ) {
} else if ( !(current_state == assumed_receiver_state->state)
&& (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) {
next_send_time = sent_states.back().timestamp + send_interval(); next_send_time = sent_states.back().timestamp + send_interval();
if ( mindelay_clock != uint64_t( -1 ) ) { if ( mindelay_clock != uint64_t( -1 ) ) {
next_send_time = std::max( next_send_time, mindelay_clock + SEND_MINDELAY ); next_send_time = std::max( next_send_time, mindelay_clock + SEND_MINDELAY );
} }
} else if ( !(current_state == sent_states.front().state ) } else if ( !( current_state == sent_states.front().state ) && ( last_heard + ACTIVE_RETRY_TIMEOUT > now ) ) {
&& (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) {
next_send_time = sent_states.back().timestamp + connection->timeout() + ACK_DELAY; next_send_time = sent_states.back().timestamp + connection->timeout() + ACK_DELAY;
} else { } else {
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
/* speed up shutdown sequence */ /* speed up shutdown sequence */
if ( shutdown_in_progress || (ack_num == uint64_t(-1)) ) { if ( shutdown_in_progress || ( ack_num == uint64_t( -1 ) ) ) {
next_ack_time = sent_states.back().timestamp + send_interval(); next_ack_time = sent_states.back().timestamp + send_interval();
} }
} }
/* How many ms to wait until next event */ /* How many ms to wait until next event */
template <class MyState> template<class MyState>
int TransportSender<MyState>::wait_time( void ) int TransportSender<MyState>::wait_time( void )
{ {
calculate_timers(); calculate_timers();
@@ -149,7 +134,7 @@ int TransportSender<MyState>::wait_time( void )
} }
/* Send data or an empty ack if necessary */ /* Send data or an empty ack if necessary */
template <class MyState> template<class MyState>
void TransportSender<MyState>::tick( void ) void TransportSender<MyState>::tick( void )
{ {
calculate_timers(); /* updates assumed receiver state and rationalizes */ calculate_timers(); /* updates assumed receiver state and rationalizes */
@@ -160,8 +145,7 @@ void TransportSender<MyState>::tick( void )
uint64_t now = timestamp(); uint64_t now = timestamp();
if ( (now < next_ack_time) if ( ( now < next_ack_time ) && ( now < next_send_time ) ) {
&& (now < next_send_time) ) {
return; return;
} }
@@ -187,22 +171,22 @@ void TransportSender<MyState>::tick( void )
} }
if ( diff.empty() ) { if ( diff.empty() ) {
if ( (now >= next_ack_time) ) { if ( ( now >= next_ack_time ) ) {
send_empty_ack(); send_empty_ack();
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
if ( (now >= next_send_time) ) { if ( ( now >= next_send_time ) ) {
next_send_time = uint64_t( -1 ); next_send_time = uint64_t( -1 );
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
} else if ( (now >= next_send_time) || (now >= next_ack_time) ) { } else if ( ( now >= next_send_time ) || ( now >= next_ack_time ) ) {
/* Send diffs or ack */ /* Send diffs or ack */
send_to_receiver( diff ); send_to_receiver( diff );
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_empty_ack( void ) void TransportSender<MyState>::send_empty_ack( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -221,22 +205,24 @@ void TransportSender<MyState>::send_empty_ack( void )
send_in_fragments( "", new_num ); send_in_fragments( "", new_num );
next_ack_time = now + ACK_INTERVAL; next_ack_time = now + ACK_INTERVAL;
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state ) void TransportSender<MyState>::add_sent_state( uint64_t the_timestamp, uint64_t num, MyState& state )
{ {
sent_states.push_back( TimestampedState<MyState>( the_timestamp, num, state ) ); sent_states.push_back( TimestampedState<MyState>( the_timestamp, num, state ) );
if ( sent_states.size() > 32 ) { /* limit on state queue */ if ( sent_states.size() > 32 ) { /* limit on state queue */
typename sent_states_type::iterator last = sent_states.end(); typename sent_states_type::iterator last = sent_states.end();
for ( int i = 0; i < 16; i++ ) { last--; } for ( int i = 0; i < 16; i++ ) {
last--;
}
sent_states.erase( last ); /* erase state from middle of queue */ sent_states.erase( last ); /* erase state from middle of queue */
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_to_receiver( const std::string & diff ) void TransportSender<MyState>::send_to_receiver( const std::string& diff )
{ {
uint64_t new_num; uint64_t new_num;
if ( current_state == sent_states.back().state ) { /* previously sent */ if ( current_state == sent_states.back().state ) { /* previously sent */
@@ -263,10 +249,10 @@ void TransportSender<MyState>::send_to_receiver( const std::string & diff )
assumed_receiver_state = sent_states.end(); assumed_receiver_state = sent_states.end();
assumed_receiver_state--; assumed_receiver_state--;
next_ack_time = timestamp() + ACK_INTERVAL; next_ack_time = timestamp() + ACK_INTERVAL;
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::update_assumed_receiver_state( void ) void TransportSender<MyState>::update_assumed_receiver_state( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -275,13 +261,13 @@ void TransportSender<MyState>::update_assumed_receiver_state( void )
transmitted recently enough ago */ transmitted recently enough ago */
assumed_receiver_state = sent_states.begin(); assumed_receiver_state = sent_states.begin();
typename std::list< TimestampedState<MyState> >::iterator i = sent_states.begin(); typename std::list<TimestampedState<MyState>>::iterator i = sent_states.begin();
i++; i++;
while ( i != sent_states.end() ) { while ( i != sent_states.end() ) {
assert( now >= i->timestamp ); assert( now >= i->timestamp );
if ( uint64_t(now - i->timestamp) < connection->timeout() + ACK_DELAY ) { if ( uint64_t( now - i->timestamp ) < connection->timeout() + ACK_DELAY ) {
assumed_receiver_state = i; assumed_receiver_state = i;
} else { } else {
return; return;
@@ -291,33 +277,33 @@ void TransportSender<MyState>::update_assumed_receiver_state( void )
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::rationalize_states( void ) void TransportSender<MyState>::rationalize_states( void )
{ {
const MyState * known_receiver_state = &sent_states.front().state; const MyState* known_receiver_state = &sent_states.front().state;
current_state.subtract( known_receiver_state ); current_state.subtract( known_receiver_state );
for ( typename std::list< TimestampedState<MyState> >::reverse_iterator i = sent_states.rbegin(); for ( typename std::list<TimestampedState<MyState>>::reverse_iterator i = sent_states.rbegin();
i != sent_states.rend(); i != sent_states.rend();
i++ ) { i++ ) {
i->state.subtract( known_receiver_state ); i->state.subtract( known_receiver_state );
} }
} }
template <class MyState> template<class MyState>
const std::string TransportSender<MyState>::make_chaff( void ) const std::string TransportSender<MyState>::make_chaff( void )
{ {
const size_t CHAFF_MAX = 16; const size_t CHAFF_MAX = 16;
const size_t chaff_len = prng.uint8() % (CHAFF_MAX + 1); const size_t chaff_len = prng.uint8() % ( CHAFF_MAX + 1 );
char chaff[ CHAFF_MAX ]; char chaff[CHAFF_MAX];
prng.fill( chaff, chaff_len ); prng.fill( chaff, chaff_len );
return std::string( chaff, chaff_len ); return std::string( chaff, chaff_len );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_in_fragments( const std::string & diff, uint64_t new_num ) void TransportSender<MyState>::send_in_fragments( const std::string& diff, uint64_t new_num )
{ {
Instruction inst; Instruction inst;
@@ -329,32 +315,37 @@ void TransportSender<MyState>::send_in_fragments( const std::string & diff, uint
inst.set_diff( diff ); inst.set_diff( diff );
inst.set_chaff( make_chaff() ); inst.set_chaff( make_chaff() );
if ( new_num == uint64_t(-1) ) { if ( new_num == uint64_t( -1 ) ) {
shutdown_tries++; shutdown_tries++;
} }
std::vector<Fragment> fragments = fragmenter.make_fragments( inst, connection->get_MTU() std::vector<Fragment> fragments = fragmenter.make_fragments(
- Network::Connection::ADDED_BYTES inst, connection->get_MTU() - Network::Connection::ADDED_BYTES - Crypto::Session::ADDED_BYTES );
- Crypto::Session::ADDED_BYTES ); for ( std::vector<Fragment>::iterator i = fragments.begin(); i != fragments.end(); i++ ) {
for ( std::vector<Fragment>::iterator i = fragments.begin();
i != fragments.end();
i++ ) {
connection->send( i->tostring() ); connection->send( i->tostring() );
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n", fprintf(
(unsigned int)(timestamp() % 100000), (int)inst.old_num(), (int)inst.new_num(), (int)i->id, (int)i->fragment_num, stderr,
(int)inst.ack_num(), (int)inst.throwaway_num(), (int)i->contents.size(), "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n",
(unsigned int)( timestamp() % 100000 ),
(int)inst.old_num(),
(int)inst.new_num(),
(int)i->id,
(int)i->fragment_num,
(int)inst.ack_num(),
(int)inst.throwaway_num(),
(int)i->contents.size(),
1000.0 / (double)send_interval(), 1000.0 / (double)send_interval(),
(int)connection->timeout(), connection->get_SRTT() ); (int)connection->timeout(),
connection->get_SRTT() );
} }
} }
pending_data_ack = false; pending_data_ack = false;
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num ) void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num )
{ {
/* Ignore ack if we have culled the state it's acknowledging */ /* Ignore ack if we have culled the state it's acknowledging */
@@ -380,7 +371,7 @@ void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num
} }
/* give up on getting acknowledgement for shutdown */ /* give up on getting acknowledgement for shutdown */
template <class MyState> template<class MyState>
bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const
{ {
if ( shutdown_in_progress ) { if ( shutdown_in_progress ) {
@@ -395,7 +386,7 @@ bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const
} }
/* Executed upon entry to new receiver state */ /* Executed upon entry to new receiver state */
template <class MyState> template<class MyState>
void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num ) void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num )
{ {
ack_num = s_ack_num; ack_num = s_ack_num;
@@ -403,8 +394,8 @@ void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num )
/* Investigate diff against known receiver state instead */ /* Investigate diff against known receiver state instead */
/* Mutates proposed_diff */ /* Mutates proposed_diff */
template <class MyState> template<class MyState>
void TransportSender<MyState>::attempt_prospective_resend_optimization( std::string &proposed_diff ) void TransportSender<MyState>::attempt_prospective_resend_optimization( std::string& proposed_diff )
{ {
if ( assumed_receiver_state == sent_states.begin() ) { if ( assumed_receiver_state == sent_states.begin() ) {
return; return;
@@ -416,9 +407,8 @@ void TransportSender<MyState>::attempt_prospective_resend_optimization( std::str
or if it would lengthen it by no more than 100 bytes and still be or if it would lengthen it by no more than 100 bytes and still be
less than 1000 bytes. */ less than 1000 bytes. */
if ( (resend_diff.size() <= proposed_diff.size()) if ( ( resend_diff.size() <= proposed_diff.size() )
|| ( (resend_diff.size() < 1000) || ( ( resend_diff.size() < 1000 ) && ( resend_diff.size() - proposed_diff.size() < 100 ) ) ) {
&& (resend_diff.size() - proposed_diff.size() < 100) ) ) {
assumed_receiver_state = sent_states.begin(); assumed_receiver_state = sent_states.begin();
proposed_diff = resend_diff; proposed_diff = resend_diff;
} }
+40 -31
View File
@@ -30,45 +30,44 @@
also delete it here. also delete it here.
*/ */
#ifndef TRANSPORT_SENDER_HPP #ifndef TRANSPORT_SENDER_HPP
#define TRANSPORT_SENDER_HPP #define TRANSPORT_SENDER_HPP
#include <string>
#include <list> #include <list>
#include <string>
#include "src/crypto/prng.h"
#include "src/network/network.h" #include "src/network/network.h"
#include "src/protobufs/transportinstruction.pb.h" #include "src/protobufs/transportinstruction.pb.h"
#include "transportstate.h"
#include "transportfragment.h" #include "transportfragment.h"
#include "src/crypto/prng.h" #include "transportstate.h"
namespace Network { namespace Network {
using namespace TransportBuffers; using namespace TransportBuffers;
/* timing parameters */ /* timing parameters */
const int SEND_INTERVAL_MIN = 20; /* ms between frames */ const int SEND_INTERVAL_MIN = 20; /* ms between frames */
const int SEND_INTERVAL_MAX = 250; /* ms between frames */ const int SEND_INTERVAL_MAX = 250; /* ms between frames */
const int ACK_INTERVAL = 3000; /* ms between empty acks */ const int ACK_INTERVAL = 3000; /* ms between empty acks */
const int ACK_DELAY = 100; /* ms before delayed ack */ const int ACK_DELAY = 100; /* ms before delayed ack */
const int SHUTDOWN_RETRIES = 16; /* number of shutdown packets to send before giving up */ const int SHUTDOWN_RETRIES = 16; /* number of shutdown packets to send before giving up */
const int ACTIVE_RETRY_TIMEOUT = 10000; /* attempt to resend at frame rate */ const int ACTIVE_RETRY_TIMEOUT = 10000; /* attempt to resend at frame rate */
template <class MyState> template<class MyState>
class TransportSender class TransportSender
{ {
private: private:
/* helper methods for tick() */ /* helper methods for tick() */
void update_assumed_receiver_state( void ); void update_assumed_receiver_state( void );
void attempt_prospective_resend_optimization( std::string &proposed_diff ); void attempt_prospective_resend_optimization( std::string& proposed_diff );
void rationalize_states( void ); void rationalize_states( void );
void send_to_receiver( const std::string & diff ); void send_to_receiver( const std::string& diff );
void send_empty_ack( void ); void send_empty_ack( void );
void send_in_fragments( const std::string & diff, uint64_t new_num ); void send_in_fragments( const std::string& diff, uint64_t new_num );
void add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state ); void add_sent_state( uint64_t the_timestamp, uint64_t num, MyState& state );
/* state of sender */ /* state of sender */
Connection *connection; Connection* connection;
MyState current_state; MyState current_state;
@@ -108,9 +107,9 @@ namespace Network {
uint64_t mindelay_clock; /* time of first pending change to current state */ uint64_t mindelay_clock; /* time of first pending change to current state */
public: public:
/* constructor */ /* constructor */
TransportSender( Connection *s_connection, MyState &initial_state ); TransportSender( Connection* s_connection, MyState& initial_state );
/* Send data or an ack if necessary */ /* Send data or an ack if necessary */
void tick( void ); void tick( void );
@@ -131,12 +130,22 @@ namespace Network {
void remote_heard( uint64_t ts ) { last_heard = ts; } void remote_heard( uint64_t ts ) { last_heard = ts; }
/* Starts shutdown sequence */ /* Starts shutdown sequence */
void start_shutdown( void ) { if ( !shutdown_in_progress ) { shutdown_start = timestamp(); shutdown_in_progress = true; } } void start_shutdown( void )
{
if ( !shutdown_in_progress ) {
shutdown_start = timestamp();
shutdown_in_progress = true;
}
}
/* Misc. getters and setters */ /* Misc. getters and setters */
/* Cannot modify current_state while shutdown in progress */ /* Cannot modify current_state while shutdown in progress */
MyState &get_current_state( void ) { assert( !shutdown_in_progress ); return current_state; } MyState& get_current_state( void )
void set_current_state( const MyState &x ) {
assert( !shutdown_in_progress );
return current_state;
}
void set_current_state( const MyState& x )
{ {
assert( !shutdown_in_progress ); assert( !shutdown_in_progress );
current_state = x; current_state = x;
@@ -145,8 +154,8 @@ namespace Network {
void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; } void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; }
bool get_shutdown_in_progress( void ) const { return shutdown_in_progress; } bool get_shutdown_in_progress( void ) const { return shutdown_in_progress; }
bool get_shutdown_acknowledged( void ) const { return sent_states.front().num == uint64_t(-1); } bool get_shutdown_acknowledged( void ) const { return sent_states.front().num == uint64_t( -1 ); }
bool get_counterparty_shutdown_acknowledged( void ) const { return fragmenter.last_ack_sent() == uint64_t(-1); } bool get_counterparty_shutdown_acknowledged( void ) const { return fragmenter.last_ack_sent() == uint64_t( -1 ); }
uint64_t get_sent_state_acked_timestamp( void ) const { return sent_states.front().timestamp; } uint64_t get_sent_state_acked_timestamp( void ) const { return sent_states.front().timestamp; }
uint64_t get_sent_state_acked( void ) const { return sent_states.front().num; } uint64_t get_sent_state_acked( void ) const { return sent_states.front().num; }
uint64_t get_sent_state_last( void ) const { return sent_states.back().num; } uint64_t get_sent_state_last( void ) const { return sent_states.back().num; }
@@ -158,9 +167,9 @@ namespace Network {
unsigned int send_interval( void ) const; unsigned int send_interval( void ) const;
/* nonexistent methods to satisfy -Weffc++ */ /* nonexistent methods to satisfy -Weffc++ */
TransportSender( const TransportSender &x ); TransportSender( const TransportSender& x );
TransportSender & operator=( const TransportSender &x ); TransportSender& operator=( const TransportSender& x );
}; };
} }
#endif #endif
+6 -6
View File
@@ -34,18 +34,18 @@
#define TRANSPORT_STATE_HPP #define TRANSPORT_STATE_HPP
namespace Network { namespace Network {
template <class State> template<class State>
class TimestampedState class TimestampedState
{ {
public: public:
uint64_t timestamp; uint64_t timestamp;
uint64_t num; uint64_t num;
State state; State state;
TimestampedState( uint64_t s_timestamp, uint64_t s_num, const State &s_state ) TimestampedState( uint64_t s_timestamp, uint64_t s_num, const State& s_state )
: timestamp( s_timestamp ), num( s_num ), state( s_state ) : timestamp( s_timestamp ), num( s_num ), state( s_state )
{} {}
}; };
} }
#endif #endif
+29 -31
View File
@@ -41,17 +41,15 @@ using namespace Parser;
using namespace Terminal; using namespace Terminal;
using namespace HostBuffers; using namespace HostBuffers;
string Complete::act( const string &str ) string Complete::act( const string& str )
{ {
for ( unsigned int i = 0; i < str.size(); i++ ) { for ( unsigned int i = 0; i < str.size(); i++ ) {
/* parse octet into up to three actions */ /* parse octet into up to three actions */
parser.input( str[ i ], actions ); parser.input( str[i], actions );
/* apply actions to terminal and delete them */ /* apply actions to terminal and delete them */
for ( Actions::iterator it = actions.begin(); for ( Actions::iterator it = actions.begin(); it != actions.end(); it++ ) {
it != actions.end(); Action& act = **it;
it++ ) {
Action &act = **it;
act.act_on_terminal( &terminal ); act.act_on_terminal( &terminal );
} }
actions.clear(); actions.clear();
@@ -60,7 +58,7 @@ string Complete::act( const string &str )
return terminal.read_octets_to_host(); return terminal.read_octets_to_host();
} }
string Complete::act( const Action &act ) string Complete::act( const Action& act )
{ {
/* apply action to terminal */ /* apply action to terminal */
act.act_on_terminal( &terminal ); act.act_on_terminal( &terminal );
@@ -68,26 +66,26 @@ string Complete::act( const Action &act )
} }
/* interface for Network::Transport */ /* interface for Network::Transport */
string Complete::diff_from( const Complete &existing ) const string Complete::diff_from( const Complete& existing ) const
{ {
HostBuffers::HostMessage output; HostBuffers::HostMessage output;
if ( existing.get_echo_ack() != get_echo_ack() ) { if ( existing.get_echo_ack() != get_echo_ack() ) {
assert( get_echo_ack() >= existing.get_echo_ack() ); assert( get_echo_ack() >= existing.get_echo_ack() );
Instruction *new_echo = output.add_instruction(); Instruction* new_echo = output.add_instruction();
new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() ); new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() );
} }
if ( !(existing.get_fb() == get_fb()) ) { if ( !( existing.get_fb() == get_fb() ) ) {
if ( (existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width()) if ( ( existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width() )
|| (existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height()) ) { || ( existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height() ) ) {
Instruction *new_res = output.add_instruction(); Instruction* new_res = output.add_instruction();
new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() ); new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() );
new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() ); new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() );
} }
string update = display.new_frame( true, existing.get_fb(), terminal.get_fb() ); string update = display.new_frame( true, existing.get_fb(), terminal.get_fb() );
if ( !update.empty() ) { if ( !update.empty() ) {
Instruction *new_inst = output.add_instruction(); Instruction* new_inst = output.add_instruction();
new_inst->MutableExtension( hostbytes )->set_hoststring( update ); new_inst->MutableExtension( hostbytes )->set_hoststring( update );
} }
} }
@@ -97,10 +95,10 @@ string Complete::diff_from( const Complete &existing ) const
string Complete::init_diff( void ) const string Complete::init_diff( void ) const
{ {
return diff_from( Complete( get_fb().ds.get_width(), get_fb().ds.get_height() )); return diff_from( Complete( get_fb().ds.get_width(), get_fb().ds.get_height() ) );
} }
void Complete::apply_string( const string & diff ) void Complete::apply_string( const string& diff )
{ {
HostBuffers::HostMessage input; HostBuffers::HostMessage input;
fatal_assert( input.ParseFromString( diff ) ); fatal_assert( input.ParseFromString( diff ) );
@@ -120,10 +118,10 @@ void Complete::apply_string( const string & diff )
} }
} }
bool Complete::operator==( Complete const &x ) const bool Complete::operator==( Complete const& x ) const
{ {
// assert( parser == x.parser ); /* parser state is irrelevant for us */ // assert( parser == x.parser ); /* parser state is irrelevant for us */
return (terminal == x.terminal) && (echo_ack == x.echo_ack); return ( terminal == x.terminal ) && ( echo_ack == x.echo_ack );
} }
bool Complete::set_echo_ack( uint64_t now ) bool Complete::set_echo_ack( uint64_t now )
@@ -131,16 +129,13 @@ bool Complete::set_echo_ack( uint64_t now )
bool ret = false; bool ret = false;
uint64_t newest_echo_ack = 0; uint64_t newest_echo_ack = 0;
for ( input_history_type::const_iterator i = input_history.begin(); for ( input_history_type::const_iterator i = input_history.begin(); i != input_history.end(); i++ ) {
i != input_history.end();
i++ ) {
if ( i->second <= now - ECHO_TIMEOUT ) { if ( i->second <= now - ECHO_TIMEOUT ) {
newest_echo_ack = i->first; newest_echo_ack = i->first;
} }
} }
for ( input_history_type::iterator i = input_history.begin(); for ( input_history_type::iterator i = input_history.begin(); i != input_history.end(); ) {
i != input_history.end(); ) {
input_history_type::iterator i_next = i; input_history_type::iterator i_next = i;
i_next++; i_next++;
if ( i->first < newest_echo_ack ) { if ( i->first < newest_echo_ack ) {
@@ -179,11 +174,11 @@ int Complete::wait_time( uint64_t now ) const
return next_echo_ack_time - now; return next_echo_ack_time - now;
} }
bool Complete::compare( const Complete &other ) const bool Complete::compare( const Complete& other ) const
{ {
bool ret = false; bool ret = false;
const Framebuffer &fb = terminal.get_fb(); const Framebuffer& fb = terminal.get_fb();
const Framebuffer &other_fb = other.terminal.get_fb(); const Framebuffer& other_fb = other.terminal.get_fb();
const int height = fb.ds.get_height(); const int height = fb.ds.get_height();
const int other_height = other_fb.ds.get_height(); const int other_height = other_fb.ds.get_height();
const int width = fb.ds.get_width(); const int width = fb.ds.get_width();
@@ -203,11 +198,14 @@ bool Complete::compare( const Complete &other ) const
} }
} }
if ( (fb.ds.get_cursor_row() != other_fb.ds.get_cursor_row()) if ( ( fb.ds.get_cursor_row() != other_fb.ds.get_cursor_row() )
|| (fb.ds.get_cursor_col() != other_fb.ds.get_cursor_col()) ) { || ( fb.ds.get_cursor_col() != other_fb.ds.get_cursor_col() ) ) {
fprintf( stderr, "Cursor mismatch: (%d, %d) vs. (%d, %d).\n", fprintf( stderr,
fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), "Cursor mismatch: (%d, %d) vs. (%d, %d).\n",
other_fb.ds.get_cursor_row(), other_fb.ds.get_cursor_col() ); fb.ds.get_cursor_row(),
fb.ds.get_cursor_col(),
other_fb.ds.get_cursor_row(),
other_fb.ds.get_cursor_col() );
ret = true; ret = true;
} }
/* XXX should compare other terminal state too (mouse mode, bell. etc.) */ /* XXX should compare other terminal state too (mouse mode, bell. etc.) */
+16 -14
View File
@@ -42,8 +42,9 @@
/* This class represents the complete terminal -- a UTF8Parser feeding Actions to an Emulator. */ /* This class represents the complete terminal -- a UTF8Parser feeding Actions to an Emulator. */
namespace Terminal { namespace Terminal {
class Complete { class Complete
private: {
private:
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Terminal::Emulator terminal; Terminal::Emulator terminal;
Terminal::Display display; Terminal::Display display;
@@ -59,14 +60,15 @@ namespace Terminal {
static const int ECHO_TIMEOUT = 50; /* for late ack */ static const int ECHO_TIMEOUT = 50; /* for late ack */
public: public:
Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ), Complete( size_t width, size_t height )
actions(), input_history(), echo_ack( 0 ) {} : parser(), terminal( width, height ), display( false ), actions(), input_history(), echo_ack( 0 )
{}
std::string act( const std::string &str ); std::string act( const std::string& str );
std::string act( const Parser::Action &act ); std::string act( const Parser::Action& act );
const Framebuffer & get_fb( void ) const { return terminal.get_fb(); } const Framebuffer& get_fb( void ) const { return terminal.get_fb(); }
void reset_input( void ) { parser.reset_input(); } void reset_input( void ) { parser.reset_input(); }
uint64_t get_echo_ack( void ) const { return echo_ack; } uint64_t get_echo_ack( void ) const { return echo_ack; }
bool set_echo_ack( uint64_t now ); bool set_echo_ack( uint64_t now );
@@ -74,14 +76,14 @@ namespace Terminal {
int wait_time( uint64_t now ) const; int wait_time( uint64_t now ) const;
/* interface for Network::Transport */ /* interface for Network::Transport */
void subtract( const Complete * ) const {} void subtract( const Complete* ) const {}
std::string diff_from( const Complete &existing ) const; std::string diff_from( const Complete& existing ) const;
std::string init_diff( void ) const; std::string init_diff( void ) const;
void apply_string( const std::string & diff ); void apply_string( const std::string& diff );
bool operator==( const Complete &x ) const; bool operator==( const Complete& x ) const;
bool compare( const Complete &other ) const; bool compare( const Complete& other ) const;
}; };
} }
#endif #endif
+22 -27
View File
@@ -33,24 +33,22 @@
#include <cassert> #include <cassert>
#include <typeinfo> #include <typeinfo>
#include "src/protobufs/userinput.pb.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/protobufs/userinput.pb.h"
using namespace Parser; using namespace Parser;
using namespace Network; using namespace Network;
using namespace ClientBuffers; using namespace ClientBuffers;
void UserStream::subtract( const UserStream *prefix ) void UserStream::subtract( const UserStream* prefix )
{ {
// if we are subtracting ourself from ourself, just clear the std::deque // if we are subtracting ourself from ourself, just clear the std::deque
if ( this == prefix ) { if ( this == prefix ) {
actions.clear(); actions.clear();
return; return;
} }
for ( std::deque<UserEvent>::const_iterator i = prefix->actions.begin(); for ( std::deque<UserEvent>::const_iterator i = prefix->actions.begin(); i != prefix->actions.end(); i++ ) {
i != prefix->actions.end();
i++ ) {
assert( this != prefix ); assert( this != prefix );
assert( !actions.empty() ); assert( !actions.empty() );
assert( *i == actions.front() ); assert( *i == actions.front() );
@@ -58,13 +56,11 @@ void UserStream::subtract( const UserStream *prefix )
} }
} }
std::string UserStream::diff_from( const UserStream &existing ) const std::string UserStream::diff_from( const UserStream& existing ) const
{ {
std::deque<UserEvent>::const_iterator my_it = actions.begin(); std::deque<UserEvent>::const_iterator my_it = actions.begin();
for ( std::deque<UserEvent>::const_iterator i = existing.actions.begin(); for ( std::deque<UserEvent>::const_iterator i = existing.actions.begin(); i != existing.actions.end(); i++ ) {
i != existing.actions.end();
i++ ) {
assert( my_it != actions.end() ); assert( my_it != actions.end() );
assert( *i == *my_it ); assert( *i == *my_it );
my_it++; my_it++;
@@ -74,26 +70,25 @@ std::string UserStream::diff_from( const UserStream &existing ) const
while ( my_it != actions.end() ) { while ( my_it != actions.end() ) {
switch ( my_it->type ) { switch ( my_it->type ) {
case UserByteType: case UserByteType: {
{
char the_byte = my_it->userbyte.c; char the_byte = my_it->userbyte.c;
/* can we combine this with a previous Keystroke? */ /* can we combine this with a previous Keystroke? */
if ( (output.instruction_size() > 0) if ( ( output.instruction_size() > 0 )
&& (output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke )) ) { && ( output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke ) ) ) {
output.mutable_instruction( output.instruction_size() - 1 )->MutableExtension( keystroke )->mutable_keys()->append( std::string( &the_byte, 1 ) ); output.mutable_instruction( output.instruction_size() - 1 )
->MutableExtension( keystroke )
->mutable_keys()
->append( std::string( &the_byte, 1 ) );
} else { } else {
Instruction *new_inst = output.add_instruction(); Instruction* new_inst = output.add_instruction();
new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 ); new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 );
} }
} } break;
break; case ResizeType: {
case ResizeType: Instruction* new_inst = output.add_instruction();
{
Instruction *new_inst = output.add_instruction();
new_inst->MutableExtension( resize )->set_width( my_it->resize.width ); new_inst->MutableExtension( resize )->set_width( my_it->resize.width );
new_inst->MutableExtension( resize )->set_height( my_it->resize.height ); new_inst->MutableExtension( resize )->set_height( my_it->resize.height );
} } break;
break;
default: default:
assert( !"unexpected event type" ); assert( !"unexpected event type" );
break; break;
@@ -105,7 +100,7 @@ std::string UserStream::diff_from( const UserStream &existing ) const
return output.SerializeAsString(); return output.SerializeAsString();
} }
void UserStream::apply_string( const std::string &diff ) void UserStream::apply_string( const std::string& diff )
{ {
ClientBuffers::UserMessage input; ClientBuffers::UserMessage input;
fatal_assert( input.ParseFromString( diff ) ); fatal_assert( input.ParseFromString( diff ) );
@@ -123,13 +118,13 @@ void UserStream::apply_string( const std::string &diff )
} }
} }
const Parser::Action &UserStream::get_action( unsigned int i ) const const Parser::Action& UserStream::get_action( unsigned int i ) const
{ {
switch( actions[ i ].type ) { switch ( actions[i].type ) {
case UserByteType: case UserByteType:
return actions[ i ].userbyte; return actions[i].userbyte;
case ResizeType: case ResizeType:
return actions[ i ].resize; return actions[i].resize;
default: default:
assert( !"unexpected action type" ); assert( !"unexpected action type" );
static const Parser::Ignore nothing = Parser::Ignore(); static const Parser::Ignore nothing = Parser::Ignore();
+29 -24
View File
@@ -41,52 +41,57 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
namespace Network { namespace Network {
enum UserEventType { enum UserEventType
{
UserByteType = 0, UserByteType = 0,
ResizeType = 1 ResizeType = 1
}; };
class UserEvent class UserEvent
{ {
public: public:
UserEventType type; UserEventType type;
Parser::UserByte userbyte; Parser::UserByte userbyte;
Parser::Resize resize; Parser::Resize resize;
UserEvent( const Parser::UserByte & s_userbyte ) : type( UserByteType ), userbyte( s_userbyte ), resize( -1, -1 ) {} UserEvent( const Parser::UserByte& s_userbyte ) : type( UserByteType ), userbyte( s_userbyte ), resize( -1, -1 )
UserEvent( const Parser::Resize & s_resize ) : type( ResizeType ), userbyte( 0 ), resize( s_resize ) {} {}
UserEvent( const Parser::Resize& s_resize ) : type( ResizeType ), userbyte( 0 ), resize( s_resize ) {}
private: private:
UserEvent(); UserEvent();
public: public:
bool operator==( const UserEvent &x ) const { return ( type == x.type ) && ( userbyte == x.userbyte ) && ( resize == x.resize ); } bool operator==( const UserEvent& x ) const
};
class UserStream
{ {
private: return ( type == x.type ) && ( userbyte == x.userbyte ) && ( resize == x.resize );
}
};
class UserStream
{
private:
std::deque<UserEvent> actions; std::deque<UserEvent> actions;
public: public:
UserStream() : actions() {} UserStream() : actions() {}
void push_back( const Parser::UserByte & s_userbyte ) { actions.push_back( UserEvent( s_userbyte ) ); } void push_back( const Parser::UserByte& s_userbyte ) { actions.push_back( UserEvent( s_userbyte ) ); }
void push_back( const Parser::Resize & s_resize ) { actions.push_back( UserEvent( s_resize ) ); } void push_back( const Parser::Resize& s_resize ) { actions.push_back( UserEvent( s_resize ) ); }
bool empty( void ) const { return actions.empty(); } bool empty( void ) const { return actions.empty(); }
size_t size( void ) const { return actions.size(); } size_t size( void ) const { return actions.size(); }
const Parser::Action &get_action( unsigned int i ) const; const Parser::Action& get_action( unsigned int i ) const;
/* interface for Network::Transport */ /* interface for Network::Transport */
void subtract( const UserStream *prefix ); void subtract( const UserStream* prefix );
std::string diff_from( const UserStream &existing ) const; std::string diff_from( const UserStream& existing ) const;
std::string init_diff( void ) const { return diff_from( UserStream() ); }; std::string init_diff( void ) const { return diff_from( UserStream() ); };
void apply_string( const std::string &diff ); void apply_string( const std::string& diff );
bool operator==( const UserStream &x ) const { return actions == x.actions; } bool operator==( const UserStream& x ) const { return actions == x.actions; }
bool compare( const UserStream & ) { return false; } bool compare( const UserStream& ) { return false; }
}; };
} }
#endif #endif
+16 -20
View File
@@ -40,8 +40,7 @@
const Parser::StateFamily Parser::family; const Parser::StateFamily Parser::family;
static void append_or_delete( Parser::ActionPointer act, static void append_or_delete( Parser::ActionPointer act, Parser::Actions& vec )
Parser::Actions &vec )
{ {
assert( act ); assert( act );
@@ -50,7 +49,7 @@ static void append_or_delete( Parser::ActionPointer act,
} }
} }
void Parser::Parser::input( wchar_t ch, Actions &ret ) void Parser::Parser::input( wchar_t ch, Actions& ret )
{ {
Transition tx = state->input( ch ); Transition tx = state->input( ch );
@@ -66,24 +65,23 @@ void Parser::Parser::input( wchar_t ch, Actions &ret )
} }
} }
Parser::UTF8Parser::UTF8Parser() Parser::UTF8Parser::UTF8Parser() : parser(), buf_len( 0 )
: parser(), buf_len( 0 )
{ {
assert( BUF_SIZE >= (size_t)MB_CUR_MAX ); assert( BUF_SIZE >= (size_t)MB_CUR_MAX );
buf[0] = '\0'; buf[0] = '\0';
} }
void Parser::UTF8Parser::input( char c, Actions &ret ) void Parser::UTF8Parser::input( char c, Actions& ret )
{ {
assert( buf_len < BUF_SIZE ); assert( buf_len < BUF_SIZE );
/* 1-byte UTF-8 character, aka ASCII? Cheat. */ /* 1-byte UTF-8 character, aka ASCII? Cheat. */
if ( buf_len == 0 && static_cast<unsigned char>(c) <= 0x7f ) { if ( buf_len == 0 && static_cast<unsigned char>( c ) <= 0x7f ) {
parser.input( static_cast<wchar_t>(c), ret ); parser.input( static_cast<wchar_t>( c ), ret );
return; return;
} }
buf[ buf_len++ ] = c; buf[buf_len++] = c;
/* This function will only work in a UTF-8 locale. */ /* This function will only work in a UTF-8 locale. */
wchar_t pwc; wchar_t pwc;
@@ -108,19 +106,19 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
buf_len = 0; buf_len = 0;
pwc = L'\0'; pwc = L'\0';
bytes_parsed = 1; bytes_parsed = 1;
} else if ( bytes_parsed == (size_t) -1 ) { } else if ( bytes_parsed == (size_t)-1 ) {
/* invalid sequence, use replacement character and try again with last char */ /* invalid sequence, use replacement character and try again with last char */
assert( errno == EILSEQ ); assert( errno == EILSEQ );
if ( buf_len > 1 ) { if ( buf_len > 1 ) {
buf[ 0 ] = buf[ buf_len - 1 ]; buf[0] = buf[buf_len - 1];
bytes_parsed = buf_len - 1; bytes_parsed = buf_len - 1;
buf_len = 1; buf_len = 1;
} else { } else {
buf_len = 0; buf_len = 0;
bytes_parsed = 1; bytes_parsed = 1;
} }
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} else if ( bytes_parsed == (size_t) -2 ) { } else if ( bytes_parsed == (size_t)-2 ) {
/* can't parse incomplete multibyte character */ /* can't parse incomplete multibyte character */
total_bytes_parsed += buf_len; total_bytes_parsed += buf_len;
continue; continue;
@@ -137,16 +135,16 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
const uint32_t pwcheck = pwc; const uint32_t pwcheck = pwc;
if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */ if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} }
if ( (pwcheck >= 0xD800) && (pwcheck <= 0xDFFF) ) { /* surrogate code point */ if ( ( pwcheck >= 0xD800 ) && ( pwcheck <= 0xDFFF ) ) { /* surrogate code point */
/* /*
OS X unfortunately allows these sequences without EILSEQ, but OS X unfortunately allows these sequences without EILSEQ, but
they are ill-formed UTF-8 and we shouldn't repeat them to the they are ill-formed UTF-8 and we shouldn't repeat them to the
user's terminal. user's terminal.
*/ */
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} }
parser.input( pwc, ret ); parser.input( pwc, ret );
@@ -155,11 +153,9 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
} }
} }
Parser::Parser::Parser( const Parser &other ) Parser::Parser::Parser( const Parser& other ) : state( other.state ) {}
: state( other.state )
{}
Parser::Parser & Parser::Parser::operator=( const Parser &other ) Parser::Parser& Parser::Parser::operator=( const Parser& other )
{ {
state = other.state; state = other.state;
return *this; return *this;
+21 -23
View File
@@ -39,47 +39,45 @@
#include <cstring> #include <cstring>
#include <cwchar> #include <cwchar>
#include "parsertransition.h"
#include "src/terminal/parseraction.h"
#include "parserstate.h" #include "parserstate.h"
#include "parserstatefamily.h" #include "parserstatefamily.h"
#include "parsertransition.h"
#include "src/terminal/parseraction.h"
namespace Parser { namespace Parser {
extern const StateFamily family; extern const StateFamily family;
class Parser { class Parser
private: {
State const *state; private:
State const* state;
public: public:
Parser() : state( &family.s_Ground ) {} Parser() : state( &family.s_Ground ) {}
Parser( const Parser &other ); Parser( const Parser& other );
Parser & operator=( const Parser & ); Parser& operator=( const Parser& );
~Parser() {} ~Parser() {}
void input( wchar_t ch, Actions &actions ); void input( wchar_t ch, Actions& actions );
void reset_input( void ) void reset_input( void ) { state = &family.s_Ground; }
{ };
state = &family.s_Ground;
}
}; static const size_t BUF_SIZE = 8;
static const size_t BUF_SIZE = 8; class UTF8Parser
{
class UTF8Parser { private:
private:
Parser parser; Parser parser;
char buf[ BUF_SIZE ]; char buf[BUF_SIZE];
size_t buf_len; size_t buf_len;
public: public:
UTF8Parser(); UTF8Parser();
void input( char c, Actions &actions ); void input( char c, Actions& actions );
void reset_input( void ) void reset_input( void )
{ {
@@ -87,7 +85,7 @@ namespace Parser {
buf[0] = '\0'; buf[0] = '\0';
buf_len = 0; buf_len = 0;
} }
}; };
} }
#endif #endif
+13 -14
View File
@@ -38,63 +38,62 @@
using namespace Parser; using namespace Parser;
void Print::act_on_terminal( Terminal::Emulator *emu ) const void Print::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->print( this ); emu->print( this );
} }
void Execute::act_on_terminal( Terminal::Emulator *emu ) const void Execute::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->execute( this ); emu->execute( this );
} }
void Clear::act_on_terminal( Terminal::Emulator *emu ) const void Clear::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.clear( this ); emu->dispatch.clear( this );
} }
void Param::act_on_terminal( Terminal::Emulator *emu ) const void Param::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.newparamchar( this ); emu->dispatch.newparamchar( this );
} }
void Collect::act_on_terminal( Terminal::Emulator *emu ) const void Collect::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.collect( this ); emu->dispatch.collect( this );
} }
void CSI_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const void CSI_Dispatch::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->CSI_dispatch( this ); emu->CSI_dispatch( this );
} }
void Esc_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const void Esc_Dispatch::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->Esc_dispatch( this ); emu->Esc_dispatch( this );
} }
void OSC_Put::act_on_terminal( Terminal::Emulator *emu ) const void OSC_Put::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.OSC_put( this ); emu->dispatch.OSC_put( this );
} }
void OSC_Start::act_on_terminal( Terminal::Emulator *emu ) const void OSC_Start::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.OSC_start( this ); emu->dispatch.OSC_start( this );
} }
void OSC_End::act_on_terminal( Terminal::Emulator *emu ) const void OSC_End::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->OSC_end( this ); emu->OSC_end( this );
} }
void UserByte::act_on_terminal( Terminal::Emulator *emu ) const void UserByte::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->fb.ds.application_mode_cursor_keys ) );
emu->fb.ds.application_mode_cursor_keys ) );
} }
void Resize::act_on_terminal( Terminal::Emulator *emu ) const void Resize::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->resize( width, height ); emu->resize( width, height );
} }
+90 -80
View File
@@ -38,128 +38,138 @@
#include <vector> #include <vector>
namespace Terminal { namespace Terminal {
class Emulator; class Emulator;
} }
namespace Parser { namespace Parser {
class Action class Action
{ {
public: public:
wchar_t ch; wchar_t ch;
bool char_present; bool char_present;
virtual std::string name( void ) = 0; virtual std::string name( void ) = 0;
virtual void act_on_terminal( Terminal::Emulator * ) const {}; virtual void act_on_terminal( Terminal::Emulator* ) const {};
virtual bool ignore() const { return false; } virtual bool ignore() const { return false; }
Action() : ch( -1 ), char_present( false ) {}; Action() : ch( -1 ), char_present( false ) {};
virtual ~Action() {}; virtual ~Action() {};
}; };
using ActionPointer = std::shared_ptr<Action>; using ActionPointer = std::shared_ptr<Action>;
using Actions = std::vector<ActionPointer>; using Actions = std::vector<ActionPointer>;
class Ignore : public Action { class Ignore : public Action
public: {
public:
std::string name( void ) { return std::string( "Ignore" ); } std::string name( void ) { return std::string( "Ignore" ); }
bool ignore() const { return true; } bool ignore() const { return true; }
}; };
class Print : public Action { class Print : public Action
public: {
public:
std::string name( void ) { return std::string( "Print" ); } std::string name( void ) { return std::string( "Print" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Execute : public Action { class Execute : public Action
public: {
public:
std::string name( void ) { return std::string( "Execute" ); } std::string name( void ) { return std::string( "Execute" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Clear : public Action { class Clear : public Action
public: {
public:
std::string name( void ) { return std::string( "Clear" ); } std::string name( void ) { return std::string( "Clear" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Collect : public Action { class Collect : public Action
public: {
public:
std::string name( void ) { return std::string( "Collect" ); } std::string name( void ) { return std::string( "Collect" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Param : public Action { class Param : public Action
public: {
public:
std::string name( void ) { return std::string( "Param" ); } std::string name( void ) { return std::string( "Param" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Esc_Dispatch : public Action { class Esc_Dispatch : public Action
public: {
public:
std::string name( void ) { return std::string( "Esc_Dispatch" ); } std::string name( void ) { return std::string( "Esc_Dispatch" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class CSI_Dispatch : public Action { class CSI_Dispatch : public Action
public: {
public:
std::string name( void ) { return std::string( "CSI_Dispatch" ); } std::string name( void ) { return std::string( "CSI_Dispatch" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Hook : public Action { class Hook : public Action
public: std::string name( void ) { return std::string( "Hook" ); } {
}; public:
class Put : public Action { std::string name( void ) { return std::string( "Hook" ); }
public: std::string name( void ) { return std::string( "Put" ); } };
}; class Put : public Action
class Unhook : public Action { {
public: std::string name( void ) { return std::string( "Unhook" ); } public:
}; std::string name( void ) { return std::string( "Put" ); }
class OSC_Start : public Action { };
public: class Unhook : public Action
{
public:
std::string name( void ) { return std::string( "Unhook" ); }
};
class OSC_Start : public Action
{
public:
std::string name( void ) { return std::string( "OSC_Start" ); } std::string name( void ) { return std::string( "OSC_Start" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class OSC_Put : public Action { class OSC_Put : public Action
public: {
public:
std::string name( void ) { return std::string( "OSC_Put" ); } std::string name( void ) { return std::string( "OSC_Put" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class OSC_End : public Action { class OSC_End : public Action
public: {
public:
std::string name( void ) { return std::string( "OSC_End" ); } std::string name( void ) { return std::string( "OSC_End" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class UserByte : public Action { class UserByte : public Action
{
/* user keystroke -- not part of the host-source state machine*/ /* user keystroke -- not part of the host-source state machine*/
public: public:
char c; /* The user-source byte. We don't try to interpret the charset */ char c; /* The user-source byte. We don't try to interpret the charset */
std::string name( void ) { return std::string( "UserByte" ); } std::string name( void ) { return std::string( "UserByte" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
UserByte( int s_c ) : c( s_c ) {} UserByte( int s_c ) : c( s_c ) {}
bool operator==( const UserByte &other ) const bool operator==( const UserByte& other ) const { return c == other.c; }
{ };
return c == other.c;
}
};
class Resize : public Action { class Resize : public Action
{
/* resize event -- not part of the host-source state machine*/ /* resize event -- not part of the host-source state machine*/
public: public:
size_t width, height; size_t width, height;
std::string name( void ) { return std::string( "Resize" ); } std::string name( void ) { return std::string( "Resize" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
Resize( size_t s_width, size_t s_height ) Resize( size_t s_width, size_t s_height ) : width( s_width ), height( s_height ) {}
: width( s_width ),
height( s_height )
{}
bool operator==( const Resize &other ) const bool operator==( const Resize& other ) const { return ( width == other.width ) && ( height == other.height ); }
{ };
return ( width == other.width ) && ( height == other.height );
}
};
} }
#endif #endif
+40 -49
View File
@@ -39,16 +39,14 @@ using namespace Parser;
Transition State::anywhere_rule( wchar_t ch ) const Transition State::anywhere_rule( wchar_t ch ) const
{ {
if ( (ch == 0x18) || (ch == 0x1A) if ( ( ch == 0x18 ) || ( ch == 0x1A ) || ( ( 0x80 <= ch ) && ( ch <= 0x8F ) )
|| ((0x80 <= ch) && (ch <= 0x8F)) || ( ( 0x91 <= ch ) && ( ch <= 0x97 ) ) || ( ch == 0x99 ) || ( ch == 0x9A ) ) {
|| ((0x91 <= ch) && (ch <= 0x97))
|| (ch == 0x99) || (ch == 0x9A) ) {
return Transition( std::make_shared<Execute>(), &family->s_Ground ); return Transition( std::make_shared<Execute>(), &family->s_Ground );
} else if ( ch == 0x9C ) { } else if ( ch == 0x9C ) {
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} else if ( ch == 0x1B ) { } else if ( ch == 0x1B ) {
return Transition( &family->s_Escape ); return Transition( &family->s_Escape );
} else if ( (ch == 0x98) || (ch == 0x9E) || (ch == 0x9F) ) { } else if ( ( ch == 0x98 ) || ( ch == 0x9E ) || ( ch == 0x9F ) ) {
return Transition( &family->s_SOS_PM_APC_String ); return Transition( &family->s_SOS_PM_APC_String );
} else if ( ch == 0x90 ) { } else if ( ch == 0x90 ) {
return Transition( &family->s_DCS_Entry ); return Transition( &family->s_DCS_Entry );
@@ -58,7 +56,7 @@ Transition State::anywhere_rule( wchar_t ch ) const
return Transition( &family->s_CSI_Entry ); return Transition( &family->s_CSI_Entry );
} }
return Transition(( State * )NULL, ActionPointer() ); /* don't allocate an Ignore action */ return Transition( (State*)NULL, ActionPointer() ); /* don't allocate an Ignore action */
} }
Transition State::input( wchar_t ch ) const Transition State::input( wchar_t ch ) const
@@ -80,15 +78,13 @@ Transition State::input( wchar_t ch ) const
static bool C0_prime( wchar_t ch ) static bool C0_prime( wchar_t ch )
{ {
return (ch <= 0x17) return ( ch <= 0x17 ) || ( ch == 0x19 ) || ( ( 0x1C <= ch ) && ( ch <= 0x1F ) );
|| (ch == 0x19)
|| ( (0x1C <= ch) && (ch <= 0x1F) );
} }
static bool GLGR ( wchar_t ch ) static bool GLGR( wchar_t ch )
{ {
return ( (0x20 <= ch) && (ch <= 0x7F) ) /* GL area */ return ( ( 0x20 <= ch ) && ( ch <= 0x7F ) ) /* GL area */
|| ( (0xA0 <= ch) && (ch <= 0xFF) ); /* GR area */ || ( ( 0xA0 <= ch ) && ( ch <= 0xFF ) ); /* GR area */
} }
Transition Ground::input_state_rule( wchar_t ch ) const Transition Ground::input_state_rule( wchar_t ch ) const
@@ -115,16 +111,12 @@ Transition Escape::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_Escape_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_Escape_Intermediate );
} }
if ( ( (0x30 <= ch) && (ch <= 0x4F) ) if ( ( ( 0x30 <= ch ) && ( ch <= 0x4F ) ) || ( ( 0x51 <= ch ) && ( ch <= 0x57 ) ) || ( ch == 0x59 )
|| ( (0x51 <= ch) && (ch <= 0x57) ) || ( ch == 0x5A ) || ( ch == 0x5C ) || ( ( 0x60 <= ch ) && ( ch <= 0x7E ) ) ) {
|| ( ch == 0x59 )
|| ( ch == 0x5A )
|| ( ch == 0x5C )
|| ( (0x60 <= ch) && (ch <= 0x7E) ) ) {
return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground );
} }
@@ -140,7 +132,7 @@ Transition Escape::input_state_rule( wchar_t ch ) const
return Transition( &family->s_DCS_Entry ); return Transition( &family->s_DCS_Entry );
} }
if ( (ch == 0x58) || (ch == 0x5E) || (ch == 0x5F) ) { if ( ( ch == 0x58 ) || ( ch == 0x5E ) || ( ch == 0x5F ) ) {
return Transition( &family->s_SOS_PM_APC_String ); return Transition( &family->s_SOS_PM_APC_String );
} }
@@ -153,11 +145,11 @@ Transition Escape_Intermediate::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x30 <= ch) && (ch <= 0x7E) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground );
} }
@@ -175,16 +167,15 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
|| ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>(), &family->s_CSI_Param ); return Transition( std::make_shared<Param>(), &family->s_CSI_Param );
} }
if ( (0x3C <= ch) && (ch <= 0x3F) ) { if ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Param ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Param );
} }
@@ -192,7 +183,7 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate );
} }
@@ -205,19 +196,19 @@ Transition CSI_Param::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>() ); return Transition( std::make_shared<Param>() );
} }
if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { if ( ( ch == 0x3A ) || ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) ) {
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
@@ -230,15 +221,15 @@ Transition CSI_Intermediate::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
if ( (0x30 <= ch) && (ch <= 0x3F) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x3F ) ) {
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
@@ -251,7 +242,7 @@ Transition CSI_Ignore::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} }
@@ -265,7 +256,7 @@ ActionPointer DCS_Entry::enter( void ) const
Transition DCS_Entry::input_state_rule( wchar_t ch ) const Transition DCS_Entry::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate );
} }
@@ -273,15 +264,15 @@ Transition DCS_Entry::input_state_rule( wchar_t ch ) const
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>(), &family->s_DCS_Param ); return Transition( std::make_shared<Param>(), &family->s_DCS_Param );
} }
if ( (0x3C <= ch) && (ch <= 0x3F) ) { if ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Param ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Param );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
@@ -290,19 +281,19 @@ Transition DCS_Entry::input_state_rule( wchar_t ch ) const
Transition DCS_Param::input_state_rule( wchar_t ch ) const Transition DCS_Param::input_state_rule( wchar_t ch ) const
{ {
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>() ); return Transition( std::make_shared<Param>() );
} }
if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { if ( ( ch == 0x3A ) || ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) ) {
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
@@ -311,15 +302,15 @@ Transition DCS_Param::input_state_rule( wchar_t ch ) const
Transition DCS_Intermediate::input_state_rule( wchar_t ch ) const Transition DCS_Intermediate::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
if ( (0x30 <= ch) && (ch <= 0x3F) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x3F ) ) {
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
@@ -338,7 +329,7 @@ ActionPointer DCS_Passthrough::exit( void ) const
Transition DCS_Passthrough::input_state_rule( wchar_t ch ) const Transition DCS_Passthrough::input_state_rule( wchar_t ch ) const
{ {
if ( C0_prime( ch ) || ( (0x20 <= ch) && (ch <= 0x7E) ) ) { if ( C0_prime( ch ) || ( ( 0x20 <= ch ) && ( ch <= 0x7E ) ) ) {
return Transition( std::make_shared<Put>() ); return Transition( std::make_shared<Put>() );
} }
@@ -370,11 +361,11 @@ ActionPointer OSC_String::exit( void ) const
Transition OSC_String::input_state_rule( wchar_t ch ) const Transition OSC_String::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x7F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x7F ) ) {
return Transition( std::make_shared<OSC_Put>() ); return Transition( std::make_shared<OSC_Put>() );
} }
if ( (ch == 0x9C) || (ch == 0x07) ) { /* 0x07 is xterm non-ANSI variant */ if ( ( ch == 0x9C ) || ( ch == 0x07 ) ) { /* 0x07 is xterm non-ANSI variant */
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} }
+53 -39
View File
@@ -36,19 +36,19 @@
#include "parsertransition.h" #include "parsertransition.h"
namespace Parser { namespace Parser {
class StateFamily; class StateFamily;
class State class State
{ {
protected: protected:
virtual Transition input_state_rule( wchar_t ch ) const = 0; virtual Transition input_state_rule( wchar_t ch ) const = 0;
StateFamily *family; StateFamily* family;
private: private:
Transition anywhere_rule( wchar_t ch ) const; Transition anywhere_rule( wchar_t ch ) const;
public: public:
void setfamily( StateFamily *s_family ) { family = s_family; } void setfamily( StateFamily* s_family ) { family = s_family; }
Transition input( wchar_t ch ) const; Transition input( wchar_t ch ) const;
virtual ActionPointer enter( void ) const { return std::make_shared<Ignore>(); } virtual ActionPointer enter( void ) const { return std::make_shared<Ignore>(); }
virtual ActionPointer exit( void ) const { return std::make_shared<Ignore>(); } virtual ActionPointer exit( void ) const { return std::make_shared<Ignore>(); }
@@ -56,64 +56,78 @@ namespace Parser {
State() : family( NULL ) {}; State() : family( NULL ) {};
virtual ~State() {}; virtual ~State() {};
State( const State & ); State( const State& );
State & operator=( const State & ); State& operator=( const State& );
}; };
class Ground : public State { class Ground : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class Escape : public State { class Escape : public State
{
ActionPointer enter( void ) const; ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class Escape_Intermediate : public State { class Escape_Intermediate : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class CSI_Entry : public State { class CSI_Entry : public State
{
ActionPointer enter( void ) const; ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class CSI_Param : public State { class CSI_Param : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class CSI_Intermediate : public State { class CSI_Intermediate : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class CSI_Ignore : public State { class CSI_Ignore : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class DCS_Entry : public State { class DCS_Entry : public State
{
ActionPointer enter( void ) const; ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class DCS_Param : public State { class DCS_Param : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class DCS_Intermediate : public State { class DCS_Intermediate : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class DCS_Passthrough : public State { class DCS_Passthrough : public State
{
ActionPointer enter( void ) const; ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
ActionPointer exit( void ) const; ActionPointer exit( void ) const;
}; };
class DCS_Ignore : public State { class DCS_Ignore : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class OSC_String : public State { class OSC_String : public State
{
ActionPointer enter( void ) const; ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
ActionPointer exit( void ) const; ActionPointer exit( void ) const;
}; };
class SOS_PM_APC_String : public State { class SOS_PM_APC_String : public State
{
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
} }
#endif #endif
+6 -8
View File
@@ -36,9 +36,9 @@
#include "parserstate.h" #include "parserstate.h"
namespace Parser { namespace Parser {
class StateFamily class StateFamily
{ {
public: public:
Ground s_Ground; Ground s_Ground;
Escape s_Escape; Escape s_Escape;
@@ -59,10 +59,8 @@ namespace Parser {
SOS_PM_APC_String s_SOS_PM_APC_String; SOS_PM_APC_String s_SOS_PM_APC_String;
StateFamily() StateFamily()
: s_Ground(), s_Escape(), s_Escape_Intermediate(), : s_Ground(), s_Escape(), s_Escape_Intermediate(), s_CSI_Entry(), s_CSI_Param(), s_CSI_Intermediate(),
s_CSI_Entry(), s_CSI_Param(), s_CSI_Intermediate(), s_CSI_Ignore(), s_CSI_Ignore(), s_DCS_Entry(), s_DCS_Param(), s_DCS_Intermediate(), s_DCS_Passthrough(), s_DCS_Ignore(),
s_DCS_Entry(), s_DCS_Param(), s_DCS_Intermediate(),
s_DCS_Passthrough(), s_DCS_Ignore(),
s_OSC_String(), s_SOS_PM_APC_String() s_OSC_String(), s_SOS_PM_APC_String()
{ {
s_Ground.setfamily( this ); s_Ground.setfamily( this );
@@ -80,7 +78,7 @@ namespace Parser {
s_OSC_String.setfamily( this ); s_OSC_String.setfamily( this );
s_SOS_PM_APC_String.setfamily( this ); s_SOS_PM_APC_String.setfamily( this );
} }
}; };
} }
#endif #endif
+10 -12
View File
@@ -38,37 +38,35 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
namespace Parser { namespace Parser {
class State; class State;
class Transition class Transition
{ {
public: public:
// Transition is only a courier for an Action; it should // Transition is only a courier for an Action; it should
// never create/delete one on its own. // never create/delete one on its own.
ActionPointer action; ActionPointer action;
State *next_state; State* next_state;
Transition( const Transition &x ) Transition( const Transition& x ) : action( x.action ), next_state( x.next_state ) {}
: action( x.action ), Transition& operator=( const Transition& t )
next_state( x.next_state ) {}
Transition & operator=( const Transition &t )
{ {
action = t.action; action = t.action;
next_state = t.next_state; next_state = t.next_state;
return *this; return *this;
} }
Transition( ActionPointer s_action = std::make_shared<Ignore>(), State *s_next_state=NULL ) Transition( ActionPointer s_action = std::make_shared<Ignore>(), State* s_next_state = NULL )
: action( s_action ), next_state( s_next_state ) : action( s_action ), next_state( s_next_state )
{} {}
// This is only ever used in the 1-argument form; // This is only ever used in the 1-argument form;
// we use this instead of an initializer to // we use this instead of an initializer to
// tell Coverity the object never owns *action. // tell Coverity the object never owns *action.
Transition( State *s_next_state, ActionPointer s_action = std::make_shared<Ignore>() ) Transition( State* s_next_state, ActionPointer s_action = std::make_shared<Ignore>() )
: action( s_action ), next_state( s_next_state ) : action( s_action ), next_state( s_next_state )
{} {}
}; };
} }
#endif #endif
+16 -23
View File
@@ -41,9 +41,7 @@
using namespace Terminal; using namespace Terminal;
Emulator::Emulator( size_t s_width, size_t s_height ) Emulator::Emulator( size_t s_width, size_t s_height ) : fb( s_width, s_height ), dispatch(), user() {}
: fb( s_width, s_height ), dispatch(), user()
{}
std::string Emulator::read_octets_to_host( void ) std::string Emulator::read_octets_to_host( void )
{ {
@@ -52,12 +50,12 @@ std::string Emulator::read_octets_to_host( void )
return ret; return ret;
} }
void Emulator::execute( const Parser::Execute *act ) void Emulator::execute( const Parser::Execute* act )
{ {
dispatch.dispatch( CONTROL, act, &fb ); dispatch.dispatch( CONTROL, act, &fb );
} }
void Emulator::print( const Parser::Print *act ) void Emulator::print( const Parser::Print* act )
{ {
assert( act->char_present ); assert( act->char_present );
@@ -67,9 +65,9 @@ void Emulator::print( const Parser::Print *act )
* Check for printing ISO 8859-1 first, it's a cheap way to detect * Check for printing ISO 8859-1 first, it's a cheap way to detect
* some common narrow characters. * some common narrow characters.
*/ */
const int chwidth = ch == L'\0' ? -1 : ( Cell::isprint_iso8859_1( ch ) ? 1 : wcwidth( ch )); const int chwidth = ch == L'\0' ? -1 : ( Cell::isprint_iso8859_1( ch ) ? 1 : wcwidth( ch ) );
Cell *this_cell = fb.get_mutable_cell(); Cell* this_cell = fb.get_mutable_cell();
switch ( chwidth ) { switch ( chwidth ) {
case 1: /* normal character */ case 1: /* normal character */
@@ -79,9 +77,8 @@ void Emulator::print( const Parser::Print *act )
fb.ds.move_col( 0 ); fb.ds.move_col( 0 );
fb.move_rows_autoscroll( 1 ); fb.move_rows_autoscroll( 1 );
this_cell = NULL; this_cell = NULL;
} else if ( fb.ds.auto_wrap_mode } else if ( fb.ds.auto_wrap_mode && ( chwidth == 2 )
&& (chwidth == 2) && ( fb.ds.get_cursor_col() == fb.ds.get_width() - 1 ) ) {
&& (fb.ds.get_cursor_col() == fb.ds.get_width() - 1) ) {
/* wrap 2-cell chars if no room, even without will-wrap flag */ /* wrap 2-cell chars if no room, even without will-wrap flag */
fb.reset_cell( this_cell ); fb.reset_cell( this_cell );
fb.get_mutable_row( -1 )->set_wrap( false ); fb.get_mutable_row( -1 )->set_wrap( false );
@@ -101,7 +98,7 @@ void Emulator::print( const Parser::Print *act )
this_cell = NULL; this_cell = NULL;
} }
if (!this_cell) { if ( !this_cell ) {
this_cell = fb.get_mutable_cell(); this_cell = fb.get_mutable_cell();
} }
@@ -110,8 +107,7 @@ void Emulator::print( const Parser::Print *act )
this_cell->set_wide( chwidth == 2 ); /* chwidth had better be 1 or 2 here */ this_cell->set_wide( chwidth == 2 ); /* chwidth had better be 1 or 2 here */
fb.apply_renditions_to_cell( this_cell ); fb.apply_renditions_to_cell( this_cell );
if ( chwidth == 2 if ( chwidth == 2 && fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { /* erase overlapped cell */
&& fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { /* erase overlapped cell */
fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) );
} }
@@ -120,7 +116,7 @@ void Emulator::print( const Parser::Print *act )
break; break;
case 0: /* combining character */ case 0: /* combining character */
{ {
Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ Cell* combining_cell = fb.get_combining_cell(); /* can be null if we were resized */
if ( combining_cell == NULL ) { /* character is now offscreen */ if ( combining_cell == NULL ) { /* character is now offscreen */
break; break;
} }
@@ -138,8 +134,7 @@ void Emulator::print( const Parser::Print *act )
if ( !combining_cell->full() ) { if ( !combining_cell->full() ) {
combining_cell->append( ch ); combining_cell->append( ch );
} }
} } break;
break;
case -1: /* unprintable character */ case -1: /* unprintable character */
break; break;
default: default:
@@ -148,22 +143,20 @@ void Emulator::print( const Parser::Print *act )
} }
} }
void Emulator::CSI_dispatch( const Parser::CSI_Dispatch *act ) void Emulator::CSI_dispatch( const Parser::CSI_Dispatch* act )
{ {
dispatch.dispatch( CSI, act, &fb ); dispatch.dispatch( CSI, act, &fb );
} }
void Emulator::OSC_end( const Parser::OSC_End *act ) void Emulator::OSC_end( const Parser::OSC_End* act )
{ {
dispatch.OSC_dispatch( act, &fb ); dispatch.OSC_dispatch( act, &fb );
} }
void Emulator::Esc_dispatch( const Parser::Esc_Dispatch *act ) void Emulator::Esc_dispatch( const Parser::Esc_Dispatch* act )
{ {
/* handle 7-bit ESC-encoding of C1 control characters */ /* handle 7-bit ESC-encoding of C1 control characters */
if ( (dispatch.get_dispatch_chars().size() == 0) if ( ( dispatch.get_dispatch_chars().size() == 0 ) && ( 0x40 <= act->ch ) && ( act->ch <= 0x5F ) ) {
&& (0x40 <= act->ch)
&& (act->ch <= 0x5F) ) {
Parser::Esc_Dispatch act2 = *act; Parser::Esc_Dispatch act2 = *act;
act2.ch += 0x40; act2.ch += 0x40;
dispatch.dispatch( CONTROL, &act2, &fb ); dispatch.dispatch( CONTROL, &act2, &fb );
@@ -177,7 +170,7 @@ void Emulator::resize( size_t s_width, size_t s_height )
fb.resize( s_width, s_height ); fb.resize( s_width, s_height );
} }
bool Emulator::operator==( Emulator const &x ) const bool Emulator::operator==( Emulator const& x ) const
{ {
/* dispatcher and user are irrelevant for us */ /* dispatcher and user are irrelevant for us */
return fb == x.fb; return fb == x.fb;
+25 -24
View File
@@ -41,47 +41,48 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h" #include "terminaldispatcher.h"
#include "terminaluserinput.h"
#include "terminaldisplay.h" #include "terminaldisplay.h"
#include "terminaluserinput.h"
namespace Terminal { namespace Terminal {
class Emulator { class Emulator
friend void Parser::Print::act_on_terminal( Emulator * ) const; {
friend void Parser::Execute::act_on_terminal( Emulator * ) const; friend void Parser::Print::act_on_terminal( Emulator* ) const;
friend void Parser::Clear::act_on_terminal( Emulator * ) const; friend void Parser::Execute::act_on_terminal( Emulator* ) const;
friend void Parser::Param::act_on_terminal( Emulator * ) const; friend void Parser::Clear::act_on_terminal( Emulator* ) const;
friend void Parser::Collect::act_on_terminal( Emulator * ) const; friend void Parser::Param::act_on_terminal( Emulator* ) const;
friend void Parser::CSI_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::Collect::act_on_terminal( Emulator* ) const;
friend void Parser::Esc_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::CSI_Dispatch::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_Start::act_on_terminal( Emulator * ) const; friend void Parser::Esc_Dispatch::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_Put::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Start::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_End::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Put::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_End::act_on_terminal( Emulator* ) const;
friend void Parser::UserByte::act_on_terminal( Emulator * ) const; friend void Parser::UserByte::act_on_terminal( Emulator* ) const;
friend void Parser::Resize::act_on_terminal( Emulator * ) const; friend void Parser::Resize::act_on_terminal( Emulator* ) const;
private: private:
Framebuffer fb; Framebuffer fb;
Dispatcher dispatch; Dispatcher dispatch;
UserInput user; UserInput user;
/* action methods */ /* action methods */
void print( const Parser::Print *act ); void print( const Parser::Print* act );
void execute( const Parser::Execute *act ); void execute( const Parser::Execute* act );
void CSI_dispatch( const Parser::CSI_Dispatch *act ); void CSI_dispatch( const Parser::CSI_Dispatch* act );
void Esc_dispatch( const Parser::Esc_Dispatch *act ); void Esc_dispatch( const Parser::Esc_Dispatch* act );
void OSC_end( const Parser::OSC_End *act ); void OSC_end( const Parser::OSC_End* act );
void resize( size_t s_width, size_t s_height ); void resize( size_t s_width, size_t s_height );
public: public:
Emulator( size_t s_width, size_t s_height ); Emulator( size_t s_width, size_t s_height );
std::string read_octets_to_host( void ); std::string read_octets_to_host( void );
const Framebuffer & get_fb( void ) const { return fb; } const Framebuffer& get_fb( void ) const { return fb; }
bool operator==( Emulator const &x ) const; bool operator==( Emulator const& x ) const;
}; };
} }
#endif #endif
+41 -40
View File
@@ -36,23 +36,22 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "terminaldispatcher.h"
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h"
using namespace Terminal; using namespace Terminal;
static const size_t MAXIMUM_CLIPBOARD_SIZE = 16*1024; static const size_t MAXIMUM_CLIPBOARD_SIZE = 16 * 1024;
Dispatcher::Dispatcher() Dispatcher::Dispatcher()
: params(), parsed_params(), parsed( false ), dispatch_chars(), : params(), parsed_params(), parsed( false ), dispatch_chars(), OSC_string(), terminal_to_host()
OSC_string(), terminal_to_host()
{} {}
void Dispatcher::newparamchar( const Parser::Param *act ) void Dispatcher::newparamchar( const Parser::Param* act )
{ {
assert( act->char_present ); assert( act->char_present );
assert( (act->ch == ';') || ( (act->ch >= '0') && (act->ch <= '9') ) ); assert( ( act->ch == ';' ) || ( ( act->ch >= '0' ) && ( act->ch <= '9' ) ) );
if ( params.length() < 100 ) { if ( params.length() < 100 ) {
/* enough for 16 five-char params plus 15 semicolons */ /* enough for 16 five-char params plus 15 semicolons */
params.push_back( act->ch ); params.push_back( act->ch );
@@ -60,7 +59,7 @@ void Dispatcher::newparamchar( const Parser::Param *act )
parsed = false; parsed = false;
} }
void Dispatcher::collect( const Parser::Collect *act ) void Dispatcher::collect( const Parser::Collect* act )
{ {
assert( act->char_present ); assert( act->char_present );
if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */ if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */
@@ -69,7 +68,7 @@ void Dispatcher::collect( const Parser::Collect *act )
} }
} }
void Dispatcher::clear( const Parser::Clear *act __attribute((unused)) ) void Dispatcher::clear( const Parser::Clear* act __attribute( ( unused ) ) )
{ {
params.clear(); params.clear();
dispatch_chars.clear(); dispatch_chars.clear();
@@ -83,17 +82,17 @@ void Dispatcher::parse_params( void )
} }
parsed_params.clear(); parsed_params.clear();
const char *str = params.c_str(); const char* str = params.c_str();
const char *segment_begin = str; const char* segment_begin = str;
while ( 1 ) { while ( 1 ) {
const char *segment_end = strchr( segment_begin, ';' ); const char* segment_end = strchr( segment_begin, ';' );
if ( segment_end == NULL ) { if ( segment_end == NULL ) {
break; break;
} }
errno = 0; errno = 0;
char *endptr; char* endptr;
long val = strtol( segment_begin, &endptr, 10 ); long val = strtol( segment_begin, &endptr, 10 );
if ( endptr == segment_begin ) { if ( endptr == segment_begin ) {
val = -1; val = -1;
@@ -113,7 +112,7 @@ void Dispatcher::parse_params( void )
/* get last param */ /* get last param */
errno = 0; errno = 0;
char *endptr; char* endptr;
long val = strtol( segment_begin, &endptr, 10 ); long val = strtol( segment_begin, &endptr, 10 );
if ( endptr == segment_begin ) { if ( endptr == segment_begin ) {
val = -1; val = -1;
@@ -139,10 +138,11 @@ int Dispatcher::getparam( size_t N, int defaultval )
} }
if ( parsed_params.size() > N ) { if ( parsed_params.size() > N ) {
ret = parsed_params[ N ]; ret = parsed_params[N];
} }
if ( ret < 1 ) ret = defaultval; if ( ret < 1 )
ret = defaultval;
return ret; return ret;
} }
@@ -158,22 +158,19 @@ int Dispatcher::param_count( void )
std::string Dispatcher::str( void ) std::string Dispatcher::str( void )
{ {
char assum[ 64 ]; char assum[64];
snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", dispatch_chars.c_str(), params.c_str() );
dispatch_chars.c_str(), params.c_str() );
return std::string( assum ); return std::string( assum );
} }
/* construct on first use to avoid static initialization order crash */ /* construct on first use to avoid static initialization order crash */
DispatchRegistry & Terminal::get_global_dispatch_registry( void ) DispatchRegistry& Terminal::get_global_dispatch_registry( void )
{ {
static DispatchRegistry global_dispatch_registry; static DispatchRegistry global_dispatch_registry;
return global_dispatch_registry; return global_dispatch_registry;
} }
static void register_function( Function_Type type, static void register_function( Function_Type type, const std::string& dispatch_chars, Function f )
const std::string & dispatch_chars,
Function f )
{ {
switch ( type ) { switch ( type ) {
case ESCAPE: case ESCAPE:
@@ -188,18 +185,19 @@ static void register_function( Function_Type type,
} }
} }
Function::Function( Function_Type type, const std::string & dispatch_chars, Function::Function( Function_Type type,
void (*s_function)( Framebuffer *, Dispatcher * ), const std::string& dispatch_chars,
void ( *s_function )( Framebuffer*, Dispatcher* ),
bool s_clears_wrap_state ) bool s_clears_wrap_state )
: function( s_function ), clears_wrap_state( s_clears_wrap_state ) : function( s_function ), clears_wrap_state( s_clears_wrap_state )
{ {
register_function( type, dispatch_chars, *this ); register_function( type, dispatch_chars, *this );
} }
void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ) void Dispatcher::dispatch( Function_Type type, const Parser::Action* act, Framebuffer* fb )
{ {
/* add final char to dispatch key */ /* add final char to dispatch key */
if ( (type == ESCAPE) || (type == CSI) ) { if ( ( type == ESCAPE ) || ( type == CSI ) ) {
assert( act->char_present ); assert( act->char_present );
Parser::Collect act2; Parser::Collect act2;
act2.char_present = true; act2.char_present = true;
@@ -207,17 +205,23 @@ void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Frameb
collect( &act2 ); collect( &act2 );
} }
dispatch_map_t *map = NULL; dispatch_map_t* map = NULL;
switch ( type ) { switch ( type ) {
case ESCAPE: map = &get_global_dispatch_registry().escape; break; case ESCAPE:
case CSI: map = &get_global_dispatch_registry().CSI; break; map = &get_global_dispatch_registry().escape;
case CONTROL: map = &get_global_dispatch_registry().control; break; break;
case CSI:
map = &get_global_dispatch_registry().CSI;
break;
case CONTROL:
map = &get_global_dispatch_registry().control;
break;
} }
std::string key = dispatch_chars; std::string key = dispatch_chars;
if ( type == CONTROL ) { if ( type == CONTROL ) {
assert( act->ch <= 255 ); assert( act->ch <= 255 );
char ctrlstr[ 2 ] = { (char)act->ch, 0 }; char ctrlstr[2] = { (char)act->ch, 0 };
key = std::string( ctrlstr, 1 ); key = std::string( ctrlstr, 1 );
} }
@@ -233,25 +237,22 @@ void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Frameb
i->second.function( fb, this ); i->second.function( fb, this );
} }
void Dispatcher::OSC_put( const Parser::OSC_Put *act ) void Dispatcher::OSC_put( const Parser::OSC_Put* act )
{ {
assert( act->char_present ); assert( act->char_present );
if ( OSC_string.size() < MAXIMUM_CLIPBOARD_SIZE) { if ( OSC_string.size() < MAXIMUM_CLIPBOARD_SIZE ) {
OSC_string.push_back( act->ch ); OSC_string.push_back( act->ch );
} }
} }
void Dispatcher::OSC_start( const Parser::OSC_Start *act __attribute((unused)) ) void Dispatcher::OSC_start( const Parser::OSC_Start* act __attribute( ( unused ) ) )
{ {
OSC_string.clear(); OSC_string.clear();
} }
bool Dispatcher::operator==( const Dispatcher &x ) const bool Dispatcher::operator==( const Dispatcher& x ) const
{ {
return ( params == x.params ) return ( params == x.params ) && ( parsed_params == x.parsed_params ) && ( parsed == x.parsed )
&& ( parsed_params == x.parsed_params ) && ( dispatch_chars == x.dispatch_chars ) && ( OSC_string == x.OSC_string )
&& ( parsed == x.parsed )
&& ( dispatch_chars == x.dispatch_chars )
&& ( OSC_string == x.OSC_string )
&& ( terminal_to_host == x.terminal_to_host ); && ( terminal_to_host == x.terminal_to_host );
} }
+47 -38
View File
@@ -33,54 +33,63 @@
#ifndef TERMINALDISPATCHER_HPP #ifndef TERMINALDISPATCHER_HPP
#define TERMINALDISPATCHER_HPP #define TERMINALDISPATCHER_HPP
#include <vector>
#include <string>
#include <map> #include <map>
#include <string>
#include <vector>
namespace Parser { namespace Parser {
class Action; class Action;
class Param; class Param;
class Collect; class Collect;
class Clear; class Clear;
class Esc_Dispatch; class Esc_Dispatch;
class CSI_Dispatch; class CSI_Dispatch;
class Execute; class Execute;
class OSC_Start; class OSC_Start;
class OSC_Put; class OSC_Put;
class OSC_End; class OSC_End;
} }
namespace Terminal { namespace Terminal {
class Framebuffer; class Framebuffer;
class Dispatcher; class Dispatcher;
enum Function_Type { ESCAPE, CSI, CONTROL }; enum Function_Type
{
ESCAPE,
CSI,
CONTROL
};
class Function { class Function
public: {
public:
Function() : function( NULL ), clears_wrap_state( true ) {} Function() : function( NULL ), clears_wrap_state( true ) {}
Function( Function_Type type, const std::string & dispatch_chars, Function( Function_Type type,
void (*s_function)( Framebuffer *, Dispatcher * ), const std::string& dispatch_chars,
void ( *s_function )( Framebuffer*, Dispatcher* ),
bool s_clears_wrap_state = true ); bool s_clears_wrap_state = true );
void (*function)( Framebuffer *, Dispatcher * ); void ( *function )( Framebuffer*, Dispatcher* );
bool clears_wrap_state; bool clears_wrap_state;
}; };
using dispatch_map_t = std::map<std::string, Function>; using dispatch_map_t = std::map<std::string, Function>;
class DispatchRegistry { class DispatchRegistry
public: {
public:
dispatch_map_t escape; dispatch_map_t escape;
dispatch_map_t CSI; dispatch_map_t CSI;
dispatch_map_t control; dispatch_map_t control;
DispatchRegistry() : escape(), CSI(), control() {} DispatchRegistry() : escape(), CSI(), control() {}
}; };
DispatchRegistry & get_global_dispatch_registry( void ); DispatchRegistry& get_global_dispatch_registry( void );
class Dispatcher { class Dispatcher
private: {
private:
std::string params; std::string params;
std::vector<int> parsed_params; std::vector<int> parsed_params;
bool parsed; bool parsed;
@@ -90,7 +99,7 @@ namespace Terminal {
void parse_params( void ); void parse_params( void );
public: public:
static const int PARAM_MAX = 65535; static const int PARAM_MAX = 65535;
/* prevent evil escape sequences from causing long loops */ /* prevent evil escape sequences from causing long loops */
@@ -100,22 +109,22 @@ namespace Terminal {
int getparam( size_t N, int defaultval ); int getparam( size_t N, int defaultval );
int param_count( void ); int param_count( void );
void newparamchar( const Parser::Param *act ); void newparamchar( const Parser::Param* act );
void collect( const Parser::Collect *act ); void collect( const Parser::Collect* act );
void clear( const Parser::Clear *act ); void clear( const Parser::Clear* act );
std::string str( void ); std::string str( void );
void dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ); void dispatch( Function_Type type, const Parser::Action* act, Framebuffer* fb );
std::string get_dispatch_chars( void ) const { return dispatch_chars; } std::string get_dispatch_chars( void ) const { return dispatch_chars; }
std::vector<wchar_t> get_OSC_string( void ) const { return OSC_string; } std::vector<wchar_t> get_OSC_string( void ) const { return OSC_string; }
void OSC_put( const Parser::OSC_Put *act ); void OSC_put( const Parser::OSC_Put* act );
void OSC_start( const Parser::OSC_Start *act ); void OSC_start( const Parser::OSC_Start* act );
void OSC_dispatch( const Parser::OSC_End *act, Framebuffer *fb ); void OSC_dispatch( const Parser::OSC_End* act, Framebuffer* fb );
bool operator==( const Dispatcher &x ) const; bool operator==( const Dispatcher& x ) const;
}; };
} }
#endif #endif
+79 -100
View File
@@ -32,14 +32,14 @@
#include <cstdio> #include <cstdio>
#include "terminaldisplay.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldisplay.h"
using namespace Terminal; using namespace Terminal;
/* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */ /* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */
static const Renditions & initial_rendition( void ) static const Renditions& initial_rendition( void )
{ {
const static Renditions blank = Renditions( 0 ); const static Renditions blank = Renditions( 0 );
return blank; return blank;
@@ -54,15 +54,15 @@ std::string Display::close() const
{ {
return std::string( "\033[?1l\033[0m\033[?25h" return std::string( "\033[?1l\033[0m\033[?25h"
"\033[?1003l\033[?1002l\033[?1001l\033[?1000l" "\033[?1003l\033[?1002l\033[?1001l\033[?1000l"
"\033[?1015l\033[?1006l\033[?1005l" ) + "\033[?1015l\033[?1006l\033[?1005l" )
std::string( rmcup ? rmcup : "" ); + std::string( rmcup ? rmcup : "" );
} }
std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const std::string Display::new_frame( bool initialized, const Framebuffer& last, const Framebuffer& f ) const
{ {
FrameState frame( last ); FrameState frame( last );
char tmp[ 64 ]; char tmp[64];
/* has bell been rung? */ /* has bell been rung? */
if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) { if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) {
@@ -71,18 +71,15 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
using title_type = Terminal::Framebuffer::title_type; using title_type = Terminal::Framebuffer::title_type;
/* has icon name or window title changed? */ /* has icon name or window title changed? */
if ( has_title && f.is_title_initialized() && if ( has_title && f.is_title_initialized()
( (!initialized) && ( ( !initialized ) || ( f.get_icon_name() != frame.last_frame.get_icon_name() )
|| (f.get_icon_name() != frame.last_frame.get_icon_name()) || ( f.get_window_title() != frame.last_frame.get_window_title() ) ) ) {
|| (f.get_window_title() != frame.last_frame.get_window_title()) ) ) {
/* set icon name and window title */ /* set icon name and window title */
if ( f.get_icon_name() == f.get_window_title() ) { if ( f.get_icon_name() == f.get_window_title() ) {
/* write combined Icon Name and Window Title */ /* write combined Icon Name and Window Title */
frame.append( "\033]0;" ); frame.append( "\033]0;" );
const title_type &window_title( f.get_window_title() ); const title_type& window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin(); for ( title_type::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) {
i != window_title.end();
i++ ) {
frame.append( *i ); frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
@@ -90,50 +87,41 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
} else { } else {
/* write Icon Name */ /* write Icon Name */
frame.append( "\033]1;" ); frame.append( "\033]1;" );
const title_type &icon_name( f.get_icon_name() ); const title_type& icon_name( f.get_icon_name() );
for ( title_type::const_iterator i = icon_name.begin(); for ( title_type::const_iterator i = icon_name.begin(); i != icon_name.end(); i++ ) {
i != icon_name.end();
i++ ) {
frame.append( *i ); frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
frame.append( "\033]2;" ); frame.append( "\033]2;" );
const title_type &window_title( f.get_window_title() ); const title_type& window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin(); for ( title_type::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) {
i != window_title.end();
i++ ) {
frame.append( *i ); frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
} }
} }
/* has clipboard changed? */ /* has clipboard changed? */
if (f.get_clipboard() != frame.last_frame.get_clipboard()) { if ( f.get_clipboard() != frame.last_frame.get_clipboard() ) {
frame.append( "\033]52;c;" ); frame.append( "\033]52;c;" );
const title_type &clipboard( f.get_clipboard() ); const title_type& clipboard( f.get_clipboard() );
for ( title_type::const_iterator i = clipboard.begin(); for ( title_type::const_iterator i = clipboard.begin(); i != clipboard.end(); i++ ) {
i != clipboard.end();
i++ ) {
frame.append( *i ); frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
} }
/* has reverse video state changed? */ /* has reverse video state changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.reverse_video != frame.last_frame.ds.reverse_video ) ) {
|| (f.ds.reverse_video != frame.last_frame.ds.reverse_video) ) {
/* set reverse video */ /* set reverse video */
snprintf( tmp, 64, "\033[?5%c", (f.ds.reverse_video ? 'h' : 'l') ); snprintf( tmp, 64, "\033[?5%c", ( f.ds.reverse_video ? 'h' : 'l' ) );
frame.append( tmp ); frame.append( tmp );
} }
/* has size changed? */ /* has size changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.get_width() != frame.last_frame.ds.get_width() )
|| (f.ds.get_width() != frame.last_frame.ds.get_width()) || ( f.ds.get_height() != frame.last_frame.ds.get_height() ) ) {
|| (f.ds.get_height() != frame.last_frame.ds.get_height()) ) {
/* reset scrolling region */ /* reset scrolling region */
frame.append( "\033[r" ); frame.append( "\033[r" );
@@ -161,7 +149,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
if ( frame.last_frame.ds.get_width() < f.ds.get_width() ) { if ( frame.last_frame.ds.get_width() < f.ds.get_width() ) {
for ( Framebuffer::rows_type::iterator p = rows.begin(); p != rows.end(); p++ ) { for ( Framebuffer::rows_type::iterator p = rows.begin(); p != rows.end(); p++ ) {
*p = std::make_shared<Row>( **p ); *p = std::make_shared<Row>( **p );
(*p)->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) ); ( *p )->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) );
} }
} }
/* Add rows if we've gotten a resize and new is taller than old */ /* Add rows if we've gotten a resize and new is taller than old */
@@ -179,9 +167,9 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
int scroll_height = 0; int scroll_height = 0;
for ( int row = 0; row < f.ds.get_height(); row++ ) { for ( int row = 0; row < f.ds.get_height(); row++ ) {
const Row *new_row = f.get_row( 0 ); const Row* new_row = f.get_row( 0 );
const Row *old_row = &*rows.at( row ); const Row* old_row = &*rows.at( row );
if ( ! ( new_row == old_row || *new_row == *old_row ) ) { if ( !( new_row == old_row || *new_row == *old_row ) ) {
continue; continue;
} }
/* if row 0, we're looking at ourselves and probably didn't scroll */ /* if row 0, we're looking at ourselves and probably didn't scroll */
@@ -193,11 +181,8 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
scroll_height = 1; scroll_height = 1;
/* how big is the region that was scrolled? */ /* how big is the region that was scrolled? */
for ( int region_height = 1; for ( int region_height = 1; lines_scrolled + region_height < f.ds.get_height(); region_height++ ) {
lines_scrolled + region_height < f.ds.get_height(); if ( *f.get_row( region_height ) == *rows.at( lines_scrolled + region_height ) ) {
region_height++ ) {
if ( *f.get_row( region_height )
== *rows.at( lines_scrolled + region_height ) ) {
scroll_height = region_height + 1; scroll_height = region_height + 1;
} else { } else {
break; break;
@@ -227,15 +212,13 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
/* Common case: if we're already on the bottom line and we're scrolling the whole /* Common case: if we're already on the bottom line and we're scrolling the whole
* screen, just do a CR and LFs. * screen, just do a CR and LFs.
*/ */
if ( scroll_height + lines_scrolled == f.ds.get_height() if ( scroll_height + lines_scrolled == f.ds.get_height() && frame.cursor_y + 1 == f.ds.get_height() ) {
&& frame.cursor_y + 1 == f.ds.get_height() ) {
frame.append( '\r' ); frame.append( '\r' );
frame.append( lines_scrolled, '\n' ); frame.append( lines_scrolled, '\n' );
frame.cursor_x = 0; frame.cursor_x = 0;
} else { } else {
/* set scrolling region */ /* set scrolling region */
snprintf( tmp, 64, "\033[%d;%dr", snprintf( tmp, 64, "\033[%d;%dr", top_margin + 1, bottom_margin + 1 );
top_margin + 1, bottom_margin + 1);
frame.append( tmp ); frame.append( tmp );
/* go to bottom of scrolling region */ /* go to bottom of scrolling region */
@@ -270,15 +253,13 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
} }
/* has cursor location changed? */ /* has cursor location changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.get_cursor_row() != frame.cursor_y )
|| (f.ds.get_cursor_row() != frame.cursor_y) || ( f.ds.get_cursor_col() != frame.cursor_x ) ) {
|| (f.ds.get_cursor_col() != frame.cursor_x) ) {
frame.append_move( f.ds.get_cursor_row(), f.ds.get_cursor_col() ); frame.append_move( f.ds.get_cursor_row(), f.ds.get_cursor_col() );
} }
/* has cursor visibility changed? */ /* has cursor visibility changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.cursor_visible != frame.cursor_visible ) ) {
|| (f.ds.cursor_visible != frame.cursor_visible) ) {
if ( f.ds.cursor_visible ) { if ( f.ds.cursor_visible ) {
frame.append( "\033[?25h" ); frame.append( "\033[?25h" );
} else { } else {
@@ -290,67 +271,68 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
frame.update_rendition( f.ds.get_renditions(), !initialized ); frame.update_rendition( f.ds.get_renditions(), !initialized );
/* has bracketed paste mode changed? */ /* has bracketed paste mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste ) ) {
|| (f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste) ) {
frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" ); frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" );
} }
/* has mouse reporting mode changed? */ /* has mouse reporting mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_reporting_mode != frame.last_frame.ds.mouse_reporting_mode ) ) {
|| (f.ds.mouse_reporting_mode != frame.last_frame.ds.mouse_reporting_mode) ) { if ( f.ds.mouse_reporting_mode == DrawState::MOUSE_REPORTING_NONE ) {
if (f.ds.mouse_reporting_mode == DrawState::MOUSE_REPORTING_NONE) { frame.append( "\033[?1003l" );
frame.append("\033[?1003l"); frame.append( "\033[?1002l" );
frame.append("\033[?1002l"); frame.append( "\033[?1001l" );
frame.append("\033[?1001l"); frame.append( "\033[?1000l" );
frame.append("\033[?1000l");
} else { } else {
if (frame.last_frame.ds.mouse_reporting_mode != DrawState::MOUSE_REPORTING_NONE) { if ( frame.last_frame.ds.mouse_reporting_mode != DrawState::MOUSE_REPORTING_NONE ) {
snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_reporting_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dl", frame.last_frame.ds.mouse_reporting_mode );
frame.append(tmp); frame.append( tmp );
} }
snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_reporting_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dh", f.ds.mouse_reporting_mode );
frame.append(tmp); frame.append( tmp );
} }
} }
/* has mouse focus mode changed? */ /* has mouse focus mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_focus_event != frame.last_frame.ds.mouse_focus_event ) ) {
|| (f.ds.mouse_focus_event != frame.last_frame.ds.mouse_focus_event) ) {
frame.append( f.ds.mouse_focus_event ? "\033[?1004h" : "\033[?1004l" ); frame.append( f.ds.mouse_focus_event ? "\033[?1004h" : "\033[?1004l" );
} }
/* has mouse encoding mode changed? */ /* has mouse encoding mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_encoding_mode != frame.last_frame.ds.mouse_encoding_mode ) ) {
|| (f.ds.mouse_encoding_mode != frame.last_frame.ds.mouse_encoding_mode) ) { if ( f.ds.mouse_encoding_mode == DrawState::MOUSE_ENCODING_DEFAULT ) {
if (f.ds.mouse_encoding_mode == DrawState::MOUSE_ENCODING_DEFAULT) { frame.append( "\033[?1015l" );
frame.append("\033[?1015l"); frame.append( "\033[?1006l" );
frame.append("\033[?1006l"); frame.append( "\033[?1005l" );
frame.append("\033[?1005l");
} else { } else {
if (frame.last_frame.ds.mouse_encoding_mode != DrawState::MOUSE_ENCODING_DEFAULT) { if ( frame.last_frame.ds.mouse_encoding_mode != DrawState::MOUSE_ENCODING_DEFAULT ) {
snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_encoding_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dl", frame.last_frame.ds.mouse_encoding_mode );
frame.append(tmp); frame.append( tmp );
} }
snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_encoding_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dh", f.ds.mouse_encoding_mode );
frame.append(tmp); frame.append( tmp );
} }
} }
return frame.str; return frame.str;
} }
bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f, int frame_y, const Row &old_row, bool wrap ) const bool Display::put_row( bool initialized,
FrameState& frame,
const Framebuffer& f,
int frame_y,
const Row& old_row,
bool wrap ) const
{ {
char tmp[ 64 ]; char tmp[64];
int frame_x = 0; int frame_x = 0;
const Row &row = *f.get_row( frame_y ); const Row& row = *f.get_row( frame_y );
const Row::cells_type &cells = row.cells; const Row::cells_type& cells = row.cells;
const Row::cells_type &old_cells = old_row.cells; const Row::cells_type& old_cells = old_row.cells;
/* If we're forced to write the first column because of wrap, go ahead and do so. */ /* If we're forced to write the first column because of wrap, go ahead and do so. */
if ( wrap ) { if ( wrap ) {
const Cell &cell = cells.at( 0 ); const Cell& cell = cells.at( 0 );
frame.update_rendition( cell.get_renditions() ); frame.update_rendition( cell.get_renditions() );
frame.append_cell( cell ); frame.append_cell( cell );
frame_x += cell.get_width(); frame_x += cell.get_width();
@@ -358,7 +340,7 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
} }
/* If rows are the same object, we don't need to do anything at all. */ /* If rows are the same object, we don't need to do anything at all. */
if (initialized && &row == &old_row ) { if ( initialized && &row == &old_row ) {
return false; return false;
} }
@@ -371,12 +353,10 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
/* iterate for every cell */ /* iterate for every cell */
while ( frame_x < row_width ) { while ( frame_x < row_width ) {
const Cell &cell = cells.at( frame_x ); const Cell& cell = cells.at( frame_x );
/* Does cell need to be drawn? Skip all this. */ /* Does cell need to be drawn? Skip all this. */
if ( initialized if ( initialized && !clear_count && ( cell == old_cells.at( frame_x ) ) ) {
&& !clear_count
&& ( cell == old_cells.at( frame_x ) ) ) {
frame_x += cell.get_width(); frame_x += cell.get_width();
continue; continue;
} }
@@ -418,7 +398,6 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
} }
} }
/* Now draw a character cell. */ /* Now draw a character cell. */
/* Move to the right position. */ /* Move to the right position. */
const int cell_width = cell.get_width(); const int cell_width = cell.get_width();
@@ -460,8 +439,7 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
} }
} }
if ( ! ( wrote_last_cell if ( !( wrote_last_cell && ( frame_y < f.ds.get_height() - 1 ) ) ) {
&& (frame_y < f.ds.get_height() - 1) ) ) {
return false; return false;
} }
/* To hint that a word-select should group the end of one line /* To hint that a word-select should group the end of one line
@@ -480,9 +458,8 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
return false; return false;
} }
FrameState::FrameState( const Framebuffer &s_last ) FrameState::FrameState( const Framebuffer& s_last )
: str(), cursor_x(0), cursor_y(0), current_rendition( 0 ), : str(), cursor_x( 0 ), cursor_y( 0 ), current_rendition( 0 ), cursor_visible( s_last.ds.cursor_visible ),
cursor_visible( s_last.ds.cursor_visible ),
last_frame( s_last ) last_frame( s_last )
{ {
/* Preallocate for better performance. Make a guess-- doesn't matter for correctness */ /* Preallocate for better performance. Make a guess-- doesn't matter for correctness */
@@ -491,7 +468,8 @@ FrameState::FrameState( const Framebuffer &s_last )
void FrameState::append_silent_move( int y, int x ) void FrameState::append_silent_move( int y, int x )
{ {
if ( cursor_x == x && cursor_y == y ) return; if ( cursor_x == x && cursor_y == y )
return;
/* turn off cursor if necessary before moving cursor */ /* turn off cursor if necessary before moving cursor */
if ( cursor_visible ) { if ( cursor_visible ) {
append( "\033[?25l" ); append( "\033[?25l" );
@@ -523,13 +501,14 @@ void FrameState::append_move( int y, int x )
} }
// More optimizations are possible. // More optimizations are possible.
} }
char tmp[ 64 ]; char tmp[64];
snprintf( tmp, 64, "\033[%d;%dH", y + 1, x + 1 ); snprintf( tmp, 64, "\033[%d;%dH", y + 1, x + 1 );
append( tmp ); append( tmp );
} }
void FrameState::update_rendition(const Renditions &r, bool force) { void FrameState::update_rendition( const Renditions& r, bool force )
if ( force || !(current_rendition == r) ) { {
if ( force || !( current_rendition == r ) ) {
/* print renditions */ /* print renditions */
append_string( r.sgr() ); append_string( r.sgr() );
current_rendition = r; current_rendition = r;
+23 -16
View File
@@ -36,33 +36,35 @@
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
namespace Terminal { namespace Terminal {
/* variables used within a new_frame */ /* variables used within a new_frame */
class FrameState { class FrameState
public: {
public:
std::string str; std::string str;
int cursor_x, cursor_y; int cursor_x, cursor_y;
Renditions current_rendition; Renditions current_rendition;
bool cursor_visible; bool cursor_visible;
const Framebuffer &last_frame; const Framebuffer& last_frame;
FrameState( const Framebuffer &s_last ); FrameState( const Framebuffer& s_last );
void append( char c ) { str.append( 1, c ); } void append( char c ) { str.append( 1, c ); }
void append( size_t s, char c ) { str.append( s, c ); } void append( size_t s, char c ) { str.append( s, c ); }
void append( wchar_t wc ) { Cell::append_to_str( str, wc ); } void append( wchar_t wc ) { Cell::append_to_str( str, wc ); }
void append( const char * s ) { str.append( s ); } void append( const char* s ) { str.append( s ); }
void append_string( const std::string &append ) { str.append(append); } void append_string( const std::string& append ) { str.append( append ); }
void append_cell(const Cell & cell) { cell.print_grapheme( str ); } void append_cell( const Cell& cell ) { cell.print_grapheme( str ); }
void append_silent_move( int y, int x ); void append_silent_move( int y, int x );
void append_move( int y, int x ); void append_move( int y, int x );
void update_rendition( const Renditions &r, bool force = false ); void update_rendition( const Renditions& r, bool force = false );
}; };
class Display { class Display
private: {
private:
bool has_ech; /* erase character is part of vt200 but not supported by tmux bool has_ech; /* erase character is part of vt200 but not supported by tmux
(or by "screen" terminfo entry, which is what tmux advertises) */ (or by "screen" terminfo entry, which is what tmux advertises) */
@@ -72,16 +74,21 @@ namespace Terminal {
const char *smcup, *rmcup; /* enter and exit alternate screen mode */ const char *smcup, *rmcup; /* enter and exit alternate screen mode */
bool put_row( bool initialized, FrameState &frame, const Framebuffer &f, int frame_y, const Row &old_row, bool wrap ) const; bool put_row( bool initialized,
FrameState& frame,
const Framebuffer& f,
int frame_y,
const Row& old_row,
bool wrap ) const;
public: public:
std::string open() const; std::string open() const;
std::string close() const; std::string close() const;
std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const; std::string new_frame( bool initialized, const Framebuffer& last, const Framebuffer& f ) const;
Display( bool use_environment ); Display( bool use_environment );
}; };
} }
#endif #endif
+25 -29
View File
@@ -36,45 +36,45 @@
#include "src/include/config.h" #include "src/include/config.h"
#include "terminaldisplay.h" #include "terminaldisplay.h"
#include <string>
#include <stdexcept> #include <stdexcept>
#include <string>
#if defined HAVE_NCURSESW_CURSES_H #if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h> #include <ncursesw/curses.h>
# include <ncursesw/term.h> #include <ncursesw/term.h>
#elif defined HAVE_NCURSESW_H #elif defined HAVE_NCURSESW_H
# include <ncursesw.h> #include <ncursesw.h>
# include <term.h> #include <term.h>
#elif defined HAVE_NCURSES_CURSES_H #elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h> #include <ncurses/curses.h>
# include <ncurses/term.h> #include <ncurses/term.h>
#elif defined HAVE_NCURSES_H #elif defined HAVE_NCURSES_H
# include <ncurses.h> #include <ncurses.h>
# include <term.h> #include <term.h>
#elif defined HAVE_CURSES_H #elif defined HAVE_CURSES_H
# include <curses.h> #include <curses.h>
# include <term.h> #include <term.h>
#else #else
# error "SysV or X/Open-compatible Curses header file required" #error "SysV or X/Open-compatible Curses header file required"
#endif #endif
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
using namespace Terminal; using namespace Terminal;
static bool ti_flag( const char *capname ) static bool ti_flag( const char* capname )
{ {
int val = tigetflag( const_cast<char *>( capname ) ); int val = tigetflag( const_cast<char*>( capname ) );
if ( val == -1 ) { if ( val == -1 ) {
throw std::invalid_argument( std::string( "Invalid terminfo boolean capability " ) + capname ); throw std::invalid_argument( std::string( "Invalid terminfo boolean capability " ) + capname );
} }
return val; return val;
} }
static const char *ti_str( const char *capname ) static const char* ti_str( const char* capname )
{ {
const char *val = tigetstr( const_cast<char *>( capname ) ); const char* val = tigetstr( const_cast<char*>( capname ) );
if ( val == (const char *)-1 ) { if ( val == (const char*)-1 ) {
throw std::invalid_argument( std::string( "Invalid terminfo string capability " ) + capname ); throw std::invalid_argument( std::string( "Invalid terminfo string capability " ) + capname );
} }
return val; return val;
@@ -85,7 +85,7 @@ Display::Display( bool use_environment )
{ {
if ( use_environment ) { if ( use_environment ) {
int errret = -2; int errret = -2;
int ret = setupterm( (char *)0, 1, &errret ); int ret = setupterm( (char*)0, 1, &errret );
if ( ret != OK ) { if ( ret != OK ) {
switch ( errret ) { switch ( errret ) {
@@ -113,18 +113,14 @@ Display::Display( bool use_environment )
/* Check if we can set the window title and icon name. terminfo does not /* Check if we can set the window title and icon name. terminfo does not
have reliable information on this, so we hardcode a whitelist of have reliable information on this, so we hardcode a whitelist of
terminal type prefixes. */ terminal type prefixes. */
static const char * const title_term_types[] = { static const char* const title_term_types[]
"xterm", "rxvt", "kterm", "Eterm", "alacritty", "screen", "tmux" = { "xterm", "rxvt", "kterm", "Eterm", "alacritty", "screen", "tmux" };
};
has_title = false; has_title = false;
const char *term_type = getenv( "TERM" ); const char* term_type = getenv( "TERM" );
if ( term_type ) { if ( term_type ) {
for ( size_t i = 0; for ( size_t i = 0; i < sizeof( title_term_types ) / sizeof( const char* ); i++ ) {
i < sizeof( title_term_types ) / sizeof( const char * ); if ( 0 == strncmp( term_type, title_term_types[i], strlen( title_term_types[i] ) ) ) {
i++ ) {
if ( 0 == strncmp( term_type, title_term_types[ i ],
strlen( title_term_types[ i ] ) ) ) {
has_title = true; has_title = true;
break; break;
} }
@@ -132,8 +128,8 @@ Display::Display( bool use_environment )
} }
if ( !getenv( "MOSH_NO_TERM_INIT" ) ) { if ( !getenv( "MOSH_NO_TERM_INIT" ) ) {
smcup = ti_str("smcup"); smcup = ti_str( "smcup" );
rmcup = ti_str("rmcup"); rmcup = ti_str( "rmcup" );
} }
} }
} }
+127 -115
View File
@@ -39,11 +39,7 @@
using namespace Terminal; using namespace Terminal;
Cell::Cell( color_type background_color ) Cell::Cell( color_type background_color )
: contents(), : contents(), renditions( background_color ), wide( false ), fallback( false ), wrap( false )
renditions( background_color ),
wide( false ),
fallback( false ),
wrap( false )
{} {}
void Cell::reset( color_type background_color ) void Cell::reset( color_type background_color )
@@ -59,42 +55,40 @@ void DrawState::reinitialize_tabs( unsigned int start )
{ {
assert( default_tabs ); assert( default_tabs );
for ( unsigned int i = start; i < tabs.size(); i++ ) { for ( unsigned int i = start; i < tabs.size(); i++ ) {
tabs[ i ] = ( (i % 8) == 0 ); tabs[i] = ( ( i % 8 ) == 0 );
} }
} }
DrawState::DrawState( int s_width, int s_height ) DrawState::DrawState( int s_width, int s_height )
: width( s_width ), height( s_height ), : width( s_width ), height( s_height ), cursor_col( 0 ), cursor_row( 0 ), combining_char_col( 0 ),
cursor_col( 0 ), cursor_row( 0 ), combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_top_row( 0 ),
combining_char_col( 0 ), combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_bottom_row( height - 1 ), renditions( 0 ), save(), next_print_will_wrap( false ),
scrolling_region_top_row( 0 ), scrolling_region_bottom_row( height - 1 ), origin_mode( false ), auto_wrap_mode( true ), insert_mode( false ), cursor_visible( true ),
renditions( 0 ), save(), reverse_video( false ), bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ),
next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ), mouse_focus_event( false ), mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ),
insert_mode( false ), cursor_visible( true ), reverse_video( false ), application_mode_cursor_keys( false )
bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ), mouse_focus_event( false ),
mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), application_mode_cursor_keys( false )
{ {
reinitialize_tabs( 0 ); reinitialize_tabs( 0 );
} }
Framebuffer::Framebuffer( int s_width, int s_height ) Framebuffer::Framebuffer( int s_width, int s_height )
: rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ), ds( s_width, s_height ) : rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ),
ds( s_width, s_height )
{ {
assert( s_height > 0 ); assert( s_height > 0 );
assert( s_width > 0 ); assert( s_width > 0 );
const size_t w = s_width; const size_t w = s_width;
const color_type c = 0; const color_type c = 0;
rows = rows_type(s_height, row_pointer(std::make_shared<Row>( w, c ))); rows = rows_type( s_height, row_pointer( std::make_shared<Row>( w, c ) ) );
} }
Framebuffer::Framebuffer( const Framebuffer &other ) Framebuffer::Framebuffer( const Framebuffer& other )
: rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ), : rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ),
clipboard( other.clipboard ), bell_count( other.bell_count ), clipboard( other.clipboard ), bell_count( other.bell_count ), title_initialized( other.title_initialized ),
title_initialized( other.title_initialized ), ds( other.ds ) ds( other.ds )
{ {}
}
Framebuffer & Framebuffer::operator=( const Framebuffer &other ) Framebuffer& Framebuffer::operator=( const Framebuffer& other )
{ {
if ( this != &other ) { if ( this != &other ) {
rows = other.rows; rows = other.rows;
@@ -125,10 +119,14 @@ void DrawState::new_grapheme( void )
void DrawState::snap_cursor_to_border( void ) void DrawState::snap_cursor_to_border( void )
{ {
if ( cursor_row < limit_top() ) cursor_row = limit_top(); if ( cursor_row < limit_top() )
if ( cursor_row > limit_bottom() ) cursor_row = limit_bottom(); cursor_row = limit_top();
if ( cursor_col < 0 ) cursor_col = 0; if ( cursor_row > limit_bottom() )
if ( cursor_col >= width ) cursor_col = width - 1; cursor_row = limit_bottom();
if ( cursor_col < 0 )
cursor_col = 0;
if ( cursor_col >= width )
cursor_col = width - 1;
} }
void DrawState::move_row( int N, bool relative ) void DrawState::move_row( int N, bool relative )
@@ -157,7 +155,7 @@ void DrawState::move_col( int N, bool relative, bool implicit )
} }
if ( implicit ) { if ( implicit ) {
next_print_will_wrap = (cursor_col >= width); next_print_will_wrap = ( cursor_col >= width );
} }
snap_cursor_to_border(); snap_cursor_to_border();
@@ -170,8 +168,8 @@ void DrawState::move_col( int N, bool relative, bool implicit )
void Framebuffer::move_rows_autoscroll( int rows ) void Framebuffer::move_rows_autoscroll( int rows )
{ {
/* don't scroll if outside the scrolling region */ /* don't scroll if outside the scrolling region */
if ( (ds.get_cursor_row() < ds.get_scrolling_region_top_row()) if ( ( ds.get_cursor_row() < ds.get_scrolling_region_top_row() )
|| (ds.get_cursor_row() > ds.get_scrolling_region_bottom_row()) ) { || ( ds.get_cursor_row() > ds.get_scrolling_region_bottom_row() ) ) {
ds.move_row( rows, true ); ds.move_row( rows, true );
return; return;
} }
@@ -189,12 +187,11 @@ void Framebuffer::move_rows_autoscroll( int rows )
ds.move_row( rows, true ); ds.move_row( rows, true );
} }
Cell *Framebuffer::get_combining_cell( void ) Cell* Framebuffer::get_combining_cell( void )
{ {
if ( (ds.get_combining_char_col() < 0) if ( ( ds.get_combining_char_col() < 0 ) || ( ds.get_combining_char_row() < 0 )
|| (ds.get_combining_char_row() < 0) || ( ds.get_combining_char_col() >= ds.get_width() )
|| (ds.get_combining_char_col() >= ds.get_width()) || ( ds.get_combining_char_row() >= ds.get_height() ) ) {
|| (ds.get_combining_char_row() >= ds.get_height()) ) {
return NULL; return NULL;
} /* can happen if a resize came in between */ } /* can happen if a resize came in between */
@@ -203,26 +200,26 @@ Cell *Framebuffer::get_combining_cell( void )
void DrawState::set_tab( void ) void DrawState::set_tab( void )
{ {
tabs[ cursor_col ] = true; tabs[cursor_col] = true;
} }
void DrawState::clear_tab( int col ) void DrawState::clear_tab( int col )
{ {
tabs[ col ] = false; tabs[col] = false;
} }
int DrawState::get_next_tab( int count ) const int DrawState::get_next_tab( int count ) const
{ {
if ( count >= 0 ) { if ( count >= 0 ) {
for ( int i = cursor_col + 1; i < width; i++ ) { for ( int i = cursor_col + 1; i < width; i++ ) {
if ( tabs[ i ] && --count == 0 ) { if ( tabs[i] && --count == 0 ) {
return i; return i;
} }
} }
return -1; return -1;
} }
for ( int i = cursor_col - 1; i > 0; i-- ) { for ( int i = cursor_col - 1; i > 0; i-- ) {
if ( tabs[ i ] && ++count == 0 ) { if ( tabs[i] && ++count == 0 ) {
return i; return i;
} }
} }
@@ -238,8 +235,10 @@ void DrawState::set_scrolling_region( int top, int bottom )
scrolling_region_top_row = top; scrolling_region_top_row = top;
scrolling_region_bottom_row = bottom; scrolling_region_bottom_row = bottom;
if ( scrolling_region_top_row < 0 ) scrolling_region_top_row = 0; if ( scrolling_region_top_row < 0 )
if ( scrolling_region_bottom_row >= height ) scrolling_region_bottom_row = height - 1; scrolling_region_top_row = 0;
if ( scrolling_region_bottom_row >= height )
scrolling_region_bottom_row = height - 1;
if ( scrolling_region_bottom_row < scrolling_region_top_row ) if ( scrolling_region_bottom_row < scrolling_region_top_row )
scrolling_region_bottom_row = scrolling_region_top_row; scrolling_region_bottom_row = scrolling_region_top_row;
@@ -261,19 +260,16 @@ int DrawState::limit_bottom( void ) const
return origin_mode ? scrolling_region_bottom_row : height - 1; return origin_mode ? scrolling_region_bottom_row : height - 1;
} }
void Framebuffer::apply_renditions_to_cell( Cell *cell ) void Framebuffer::apply_renditions_to_cell( Cell* cell )
{ {
if (!cell) { if ( !cell ) {
cell = get_mutable_cell(); cell = get_mutable_cell();
} }
cell->set_renditions( ds.get_renditions() ); cell->set_renditions( ds.get_renditions() );
} }
SavedCursor::SavedCursor() SavedCursor::SavedCursor()
: cursor_col( 0 ), cursor_row( 0 ), : cursor_col( 0 ), cursor_row( 0 ), renditions( 0 ), auto_wrap_mode( true ), origin_mode( false )
renditions( 0 ),
auto_wrap_mode( true ),
origin_mode( false )
{} {}
void DrawState::save_cursor( void ) void DrawState::save_cursor( void )
@@ -299,8 +295,8 @@ void DrawState::restore_cursor( void )
void Framebuffer::insert_line( int before_row, int count ) void Framebuffer::insert_line( int before_row, int count )
{ {
if ( (before_row < ds.get_scrolling_region_top_row()) if ( ( before_row < ds.get_scrolling_region_top_row() )
|| (before_row > ds.get_scrolling_region_bottom_row() + 1) ) { || ( before_row > ds.get_scrolling_region_bottom_row() + 1 ) ) {
return; return;
} }
@@ -318,13 +314,12 @@ void Framebuffer::insert_line( int before_row, int count )
rows.erase( start, start + scroll ); rows.erase( start, start + scroll );
// insert new rows // insert new rows
start = rows.begin() + before_row; start = rows.begin() + before_row;
rows.insert( start, scroll, newrow()); rows.insert( start, scroll, newrow() );
} }
void Framebuffer::delete_line( int row, int count ) void Framebuffer::delete_line( int row, int count )
{ {
if ( (row < ds.get_scrolling_region_top_row()) if ( ( row < ds.get_scrolling_region_top_row() ) || ( row > ds.get_scrolling_region_bottom_row() ) ) {
|| (row > ds.get_scrolling_region_bottom_row()) ) {
return; return;
} }
@@ -342,7 +337,7 @@ void Framebuffer::delete_line( int row, int count )
rows.erase( start, start + scroll ); rows.erase( start, start + scroll );
// insert a block of dummy rows // insert a block of dummy rows
start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll; start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll;
rows.insert( start, scroll, newrow()); rows.insert( start, scroll, newrow() );
} }
Row::Row( const size_t s_width, const color_type background_color ) Row::Row( const size_t s_width, const color_type background_color )
@@ -407,26 +402,23 @@ void Framebuffer::resize( int s_width, int s_height )
int oldwidth = ds.get_width(); int oldwidth = ds.get_width();
ds.resize( s_width, s_height ); ds.resize( s_width, s_height );
row_pointer blankrow( newrow()); row_pointer blankrow( newrow() );
if ( oldheight != s_height ) { if ( oldheight != s_height ) {
rows.resize( s_height, blankrow ); rows.resize( s_height, blankrow );
} }
if (oldwidth == s_width) { if ( oldwidth == s_width ) {
return; return;
} }
for ( rows_type::iterator i = rows.begin(); for ( rows_type::iterator i = rows.begin(); i != rows.end() && *i != blankrow; i++ ) {
i != rows.end() && *i != blankrow;
i++ ) {
*i = std::make_shared<Row>( **i ); *i = std::make_shared<Row>( **i );
(*i)->set_wrap( false ); ( *i )->set_wrap( false );
(*i)->cells.resize( s_width, Cell( ds.get_background_rendition() ) ); ( *i )->cells.resize( s_width, Cell( ds.get_background_rendition() ) );
} }
} }
void DrawState::resize( int s_width, int s_height ) void DrawState::resize( int s_width, int s_height )
{ {
if ( (width != s_width) if ( ( width != s_width ) || ( height != s_height ) ) {
|| (height != s_height) ) {
/* reset entire scrolling region on any resize */ /* reset entire scrolling region on any resize */
/* xterm and rxvt-unicode do this. gnome-terminal only /* xterm and rxvt-unicode do this. gnome-terminal only
resets scrolling region if it has to become smaller in resize */ resets scrolling region if it has to become smaller in resize */
@@ -447,15 +439,13 @@ void DrawState::resize( int s_width, int s_height )
/* saved cursor will be snapped to border on restore */ /* saved cursor will be snapped to border on restore */
/* invalidate combining char cell if necessary */ /* invalidate combining char cell if necessary */
if ( (combining_char_col >= width) if ( ( combining_char_col >= width ) || ( combining_char_row >= height ) ) {
|| (combining_char_row >= height) ) {
combining_char_col = combining_char_row = -1; combining_char_col = combining_char_row = -1;
} }
} }
Renditions::Renditions( color_type s_background ) Renditions::Renditions( color_type s_background )
: foreground_color( 0 ), background_color( s_background ), : foreground_color( 0 ), background_color( s_background ), attributes( 0 )
attributes( 0 )
{} {}
/* This routine cannot be used to set a color beyond the 16-color set. */ /* This routine cannot be used to set a color beyond the 16-color set. */
@@ -475,35 +465,54 @@ void Renditions::set_rendition( color_type num )
return; return;
} }
if ( (30 <= num) && (num <= 37) ) { /* foreground color in 8-color set */ if ( ( 30 <= num ) && ( num <= 37 ) ) { /* foreground color in 8-color set */
foreground_color = num; foreground_color = num;
return; return;
} else if ( (40 <= num) && (num <= 47) ) { /* background color in 8-color set */ } else if ( ( 40 <= num ) && ( num <= 47 ) ) { /* background color in 8-color set */
background_color = num; background_color = num;
return; return;
} else if ( (90 <= num) && (num <= 97) ) { /* foreground color in 16-color set */ } else if ( ( 90 <= num ) && ( num <= 97 ) ) { /* foreground color in 16-color set */
foreground_color = num - 90 + 38; foreground_color = num - 90 + 38;
return; return;
} else if ( (100 <= num) && (num <= 107) ) { /* background color in 16-color set */ } else if ( ( 100 <= num ) && ( num <= 107 ) ) { /* background color in 16-color set */
background_color = num - 100 + 48; background_color = num - 100 + 48;
return; return;
} }
bool value = num < 9; bool value = num < 9;
switch ( num ) { switch ( num ) {
case 1: case 22: set_attribute(bold, value); break; case 1:
case 3: case 23: set_attribute(italic, value); break; case 22:
case 4: case 24: set_attribute(underlined, value); break; set_attribute( bold, value );
case 5: case 25: set_attribute(blink, value); break; break;
case 7: case 27: set_attribute(inverse, value); break; case 3:
case 8: case 28: set_attribute(invisible, value); break; case 23:
default: break; /* ignore unknown rendition */ set_attribute( italic, value );
break;
case 4:
case 24:
set_attribute( underlined, value );
break;
case 5:
case 25:
set_attribute( blink, value );
break;
case 7:
case 27:
set_attribute( inverse, value );
break;
case 8:
case 28:
set_attribute( invisible, value );
break;
default:
break; /* ignore unknown rendition */
} }
} }
void Renditions::set_foreground_color( int num ) void Renditions::set_foreground_color( int num )
{ {
if ( (0 <= num) && (num <= 255) ) { if ( ( 0 <= num ) && ( num <= 255 ) ) {
foreground_color = 30 + num; foreground_color = 30 + num;
} else if ( is_true_color( num ) ) { } else if ( is_true_color( num ) ) {
foreground_color = num; foreground_color = num;
@@ -512,7 +521,7 @@ void Renditions::set_foreground_color( int num )
void Renditions::set_background_color( int num ) void Renditions::set_background_color( int num )
{ {
if ( (0 <= num) && (num <= 255) ) { if ( ( 0 <= num ) && ( num <= 255 ) ) {
background_color = 40 + num; background_color = 40 + num;
} else if ( is_true_color( num ) ) { } else if ( is_true_color( num ) ) {
background_color = num; background_color = num;
@@ -525,12 +534,18 @@ std::string Renditions::sgr( void ) const
char col[64]; char col[64];
ret.append( "\033[0" ); ret.append( "\033[0" );
if ( get_attribute( bold ) ) ret.append( ";1" ); if ( get_attribute( bold ) )
if ( get_attribute( italic ) ) ret.append( ";3" ); ret.append( ";1" );
if ( get_attribute( underlined ) ) ret.append( ";4" ); if ( get_attribute( italic ) )
if ( get_attribute( blink ) ) ret.append( ";5" ); ret.append( ";3" );
if ( get_attribute( inverse ) ) ret.append( ";7" ); if ( get_attribute( underlined ) )
if ( get_attribute( invisible ) ) ret.append( ";8" ); ret.append( ";4" );
if ( get_attribute( blink ) )
ret.append( ";5" );
if ( get_attribute( inverse ) )
ret.append( ";7" );
if ( get_attribute( invisible ) )
ret.append( ";8" );
if ( foreground_color ) { if ( foreground_color ) {
// Since foreground_color is a 25-bit field, it is promoted to an int when // Since foreground_color is a 25-bit field, it is promoted to an int when
@@ -538,10 +553,12 @@ std::string Renditions::sgr( void ) const
// https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct
// printf format specifier is thus %d. // printf format specifier is thus %d.
if ( is_true_color( foreground_color ) ) { if ( is_true_color( foreground_color ) ) {
snprintf( col, sizeof( col ), ";38;2;%d;%d;%d", snprintf( col,
(foreground_color >> 16) & 0xff, sizeof( col ),
(foreground_color >> 8) & 0xff, ";38;2;%d;%d;%d",
foreground_color & 0xff); ( foreground_color >> 16 ) & 0xff,
( foreground_color >> 8 ) & 0xff,
foreground_color & 0xff );
} else if ( foreground_color > 37 ) { /* use 256-color set */ } else if ( foreground_color > 37 ) { /* use 256-color set */
snprintf( col, sizeof( col ), ";38;5;%d", foreground_color - 30 ); snprintf( col, sizeof( col ), ";38;5;%d", foreground_color - 30 );
} else { /* ANSI foreground color */ } else { /* ANSI foreground color */
@@ -557,10 +574,12 @@ std::string Renditions::sgr( void ) const
if ( background_color ) { if ( background_color ) {
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
if ( is_true_color( background_color ) ) { if ( is_true_color( background_color ) ) {
snprintf( col, sizeof( col ), ";48;2;%d;%d;%d", snprintf( col,
(background_color >> 16) & 0xff, sizeof( col ),
(background_color >> 8) & 0xff, ";48;2;%d;%d;%d",
background_color & 0xff); ( background_color >> 16 ) & 0xff,
( background_color >> 8 ) & 0xff,
background_color & 0xff );
} else if ( background_color > 47 ) { /* use 256-color set */ } else if ( background_color > 47 ) { /* use 256-color set */
snprintf( col, sizeof( col ), ";48;5;%d", background_color - 40 ); snprintf( col, sizeof( col ), ";48;5;%d", background_color - 40 );
} else { /* ANSI background color */ } else { /* ANSI background color */
@@ -578,20 +597,18 @@ std::string Renditions::sgr( void ) const
void Row::reset( color_type background_color ) void Row::reset( color_type background_color )
{ {
gen = get_gen(); gen = get_gen();
for ( cells_type::iterator i = cells.begin(); for ( cells_type::iterator i = cells.begin(); i != cells.end(); i++ ) {
i != cells.end();
i++ ) {
i->reset( background_color ); i->reset( background_color );
} }
} }
void Framebuffer::prefix_window_title( const title_type &s ) void Framebuffer::prefix_window_title( const title_type& s )
{ {
if ( icon_name == window_title ) { if ( icon_name == window_title ) {
/* preserve equivalence */ /* preserve equivalence */
icon_name.insert(icon_name.begin(), s.begin(), s.end() ); icon_name.insert( icon_name.begin(), s.begin(), s.end() );
} }
window_title.insert(window_title.begin(), s.begin(), s.end() ); window_title.insert( window_title.begin(), s.begin(), s.end() );
} }
std::string Cell::debug_contents( void ) const std::string Cell::debug_contents( void ) const
@@ -602,13 +619,11 @@ std::string Cell::debug_contents( void ) const
std::string chars( 1, '\'' ); std::string chars( 1, '\'' );
print_grapheme( chars ); print_grapheme( chars );
chars.append( "' [" ); chars.append( "' [" );
const char *lazycomma = ""; const char* lazycomma = "";
char buf[64]; char buf[64];
for ( content_type::const_iterator i = contents.begin(); for ( content_type::const_iterator i = contents.begin(); i < contents.end(); i++ ) {
i < contents.end();
i++ ) {
snprintf( buf, sizeof buf, "%s0x%02x", lazycomma, static_cast<uint8_t>(*i) ); snprintf( buf, sizeof buf, "%s0x%02x", lazycomma, static_cast<uint8_t>( *i ) );
chars.append( buf ); chars.append( buf );
lazycomma = ", "; lazycomma = ", ";
} }
@@ -616,7 +631,7 @@ std::string Cell::debug_contents( void ) const
return chars; return chars;
} }
bool Cell::compare( const Cell &other ) const bool Cell::compare( const Cell& other ) const
{ {
bool ret = false; bool ret = false;
@@ -627,13 +642,13 @@ bool Cell::compare( const Cell &other ) const
if ( grapheme != other_grapheme ) { if ( grapheme != other_grapheme ) {
ret = true; ret = true;
fprintf( stderr, "Graphemes: '%s' vs. '%s'\n", fprintf( stderr, "Graphemes: '%s' vs. '%s'\n", grapheme.c_str(), other_grapheme.c_str() );
grapheme.c_str(), other_grapheme.c_str() );
} }
if ( !contents_match( other ) ) { if ( !contents_match( other ) ) {
// ret = true; // ret = true;
fprintf( stderr, "Contents: %s (%ld) vs. %s (%ld)\n", fprintf( stderr,
"Contents: %s (%ld) vs. %s (%ld)\n",
debug_contents().c_str(), debug_contents().c_str(),
static_cast<long int>( contents.size() ), static_cast<long int>( contents.size() ),
other.debug_contents().c_str(), other.debug_contents().c_str(),
@@ -646,18 +661,16 @@ bool Cell::compare( const Cell &other ) const
// manipulated. (See [conv.prom] in various C++ standards, e.g., // manipulated. (See [conv.prom] in various C++ standards, e.g.,
// https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct
// printf format specifier is thus %d. // printf format specifier is thus %d.
fprintf( stderr, "fallback: %d vs. %d\n", fprintf( stderr, "fallback: %d vs. %d\n", fallback, other.fallback );
fallback, other.fallback );
} }
if ( wide != other.wide ) { if ( wide != other.wide ) {
ret = true; ret = true;
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
fprintf( stderr, "width: %d vs. %d\n", fprintf( stderr, "width: %d vs. %d\n", wide, other.wide );
wide, other.wide );
} }
if ( !(renditions == other.renditions) ) { if ( !( renditions == other.renditions ) ) {
ret = true; ret = true;
fprintf( stderr, "renditions differ\n" ); fprintf( stderr, "renditions differ\n" );
} }
@@ -665,8 +678,7 @@ bool Cell::compare( const Cell &other ) const
if ( wrap != other.wrap ) { if ( wrap != other.wrap ) {
ret = true; ret = true;
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
fprintf( stderr, "wrap: %d vs. %d\n", fprintf( stderr, "wrap: %d vs. %d\n", wrap, other.wrap );
wrap, other.wrap );
} }
return ret; return ret;
+119 -105
View File
@@ -45,54 +45,62 @@
/* Terminal framebuffer */ /* Terminal framebuffer */
namespace Terminal { namespace Terminal {
using color_type = uint32_t; using color_type = uint32_t;
class Renditions { class Renditions
public: {
typedef enum { bold, faint, italic, underlined, blink, inverse, invisible, SIZE } attribute_type; public:
typedef enum
{
bold,
faint,
italic,
underlined,
blink,
inverse,
invisible,
SIZE
} attribute_type;
private: private:
static const uint64_t true_color_mask = 0x1000000; static const uint64_t true_color_mask = 0x1000000;
uint64_t foreground_color : 25; uint64_t foreground_color : 25;
uint64_t background_color : 25; uint64_t background_color : 25;
uint64_t attributes : 8; uint64_t attributes : 8;
public: public:
Renditions( color_type s_background ); Renditions( color_type s_background );
void set_foreground_color( int num ); void set_foreground_color( int num );
void set_background_color( int num ); void set_background_color( int num );
void set_rendition( color_type num ); void set_rendition( color_type num );
std::string sgr( void ) const; std::string sgr( void ) const;
static unsigned int make_true_color( unsigned int r, unsigned int g, unsigned int b ) { static unsigned int make_true_color( unsigned int r, unsigned int g, unsigned int b )
return true_color_mask | (r << 16) | (g << 8) | b; {
return true_color_mask | ( r << 16 ) | ( g << 8 ) | b;
} }
static bool is_true_color(unsigned int color) { static bool is_true_color( unsigned int color ) { return ( color & true_color_mask ) != 0; }
return (color & true_color_mask) != 0;
}
// unsigned int get_foreground_rendition() const { return foreground_color; } // unsigned int get_foreground_rendition() const { return foreground_color; }
unsigned int get_background_rendition() const { return background_color; } unsigned int get_background_rendition() const { return background_color; }
bool operator==( const Renditions &x ) const bool operator==( const Renditions& x ) const
{ {
return ( attributes == x.attributes ) return ( attributes == x.attributes ) && ( foreground_color == x.foreground_color )
&& ( foreground_color == x.foreground_color )
&& ( background_color == x.background_color ); && ( background_color == x.background_color );
} }
void set_attribute( attribute_type attr, bool val ) void set_attribute( attribute_type attr, bool val )
{ {
attributes = val ? attributes = val ? ( attributes | ( 1 << attr ) ) : ( attributes & ~( 1 << attr ) );
( attributes | (1 << attr) ) :
( attributes & ~(1 << attr) );
} }
bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); } bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); }
void clear_attributes() { attributes = 0; } void clear_attributes() { attributes = 0; }
}; };
class Cell { class Cell
private: {
private:
typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */ typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */
content_type contents; content_type contents;
Renditions renditions; Renditions renditions;
@@ -100,23 +108,21 @@ namespace Terminal {
unsigned int fallback : 1; /* first character is combining character */ unsigned int fallback : 1; /* first character is combining character */
unsigned int wrap : 1; unsigned int wrap : 1;
private: private:
Cell(); Cell();
public:
public:
Cell( color_type background_color ); Cell( color_type background_color );
void reset( color_type background_color ); void reset( color_type background_color );
bool operator==( const Cell &x ) const bool operator==( const Cell& x ) const
{ {
return ( (contents == x.contents) return ( ( contents == x.contents ) && ( fallback == x.fallback ) && ( wide == x.wide )
&& (fallback == x.fallback) && ( renditions == x.renditions ) && ( wrap == x.wrap ) );
&& (wide == x.wide)
&& (renditions == x.renditions)
&& (wrap == x.wrap) );
} }
bool operator!=( const Cell &x ) const { return !operator==( x ); } bool operator!=( const Cell& x ) const { return !operator==( x ); }
/* Accessors for contents field */ /* Accessors for contents field */
std::string debug_contents( void ) const; std::string debug_contents( void ) const;
@@ -129,18 +135,15 @@ namespace Terminal {
bool is_blank( void ) const bool is_blank( void ) const
{ {
// XXX fix. // XXX fix.
return ( contents.empty() return ( contents.empty() || contents == " " || contents == "\xC2\xA0" );
|| contents == " "
|| contents == "\xC2\xA0" );
} }
bool contents_match ( const Cell &other ) const bool contents_match( const Cell& other ) const
{ {
return ( is_blank() && other.is_blank() ) return ( is_blank() && other.is_blank() ) || ( contents == other.contents );
|| ( contents == other.contents );
} }
bool compare( const Cell &other ) const; bool compare( const Cell& other ) const;
// Is this a printing ISO 8859-1 character? // Is this a printing ISO 8859-1 character?
static bool isprint_iso8859_1( const wchar_t c ) static bool isprint_iso8859_1( const wchar_t c )
@@ -148,37 +151,37 @@ namespace Terminal {
return ( c <= 0xff && c >= 0xa0 ) || ( c <= 0x7e && c >= 0x20 ); return ( c <= 0xff && c >= 0xa0 ) || ( c <= 0x7e && c >= 0x20 );
} }
static void append_to_str( std::string &dest, const wchar_t c ) static void append_to_str( std::string& dest, const wchar_t c )
{ {
/* ASCII? Cheat. */ /* ASCII? Cheat. */
if ( static_cast<uint32_t>(c) <= 0x7f ) { if ( static_cast<uint32_t>( c ) <= 0x7f ) {
dest.push_back( static_cast<char>(c) ); dest.push_back( static_cast<char>( c ) );
return; return;
} }
static mbstate_t ps = mbstate_t(); static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX]; char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps); size_t ignore = wcrtomb( NULL, 0, &ps );
(void)ignore; (void)ignore;
size_t len = wcrtomb(tmp, c, &ps); size_t len = wcrtomb( tmp, c, &ps );
dest.append( tmp, len ); dest.append( tmp, len );
} }
void append( const wchar_t c ) void append( const wchar_t c )
{ {
/* ASCII? Cheat. */ /* ASCII? Cheat. */
if ( static_cast<uint32_t>(c) <= 0x7f ) { if ( static_cast<uint32_t>( c ) <= 0x7f ) {
contents.push_back( static_cast<char>(c) ); contents.push_back( static_cast<char>( c ) );
return; return;
} }
static mbstate_t ps = mbstate_t(); static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX]; char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps); size_t ignore = wcrtomb( NULL, 0, &ps );
(void)ignore; (void)ignore;
size_t len = wcrtomb(tmp, c, &ps); size_t len = wcrtomb( tmp, c, &ps );
contents.insert( contents.end(), tmp, tmp+len ); contents.insert( contents.end(), tmp, tmp + len );
} }
void print_grapheme( std::string &output ) const void print_grapheme( std::string& output ) const
{ {
if ( contents.empty() ) { if ( contents.empty() ) {
output.append( 1, ' ' ); output.append( 1, ' ' );
@@ -205,10 +208,11 @@ namespace Terminal {
void set_fallback( bool f ) { fallback = f; } void set_fallback( bool f ) { fallback = f; }
bool get_wrap( void ) const { return wrap; } bool get_wrap( void ) const { return wrap; }
void set_wrap( bool f ) { wrap = f; } void set_wrap( bool f ) { wrap = f; }
}; };
class Row { class Row
public: {
public:
typedef std::vector<Cell> cells_type; typedef std::vector<Cell> cells_type;
cells_type cells; cells_type cells;
// gen is a generation counter. It can be used to quickly rule // gen is a generation counter. It can be used to quickly rule
@@ -216,9 +220,10 @@ namespace Terminal {
// in scrolling. // in scrolling.
uint64_t gen; uint64_t gen;
private: private:
Row(); Row();
public:
public:
Row( const size_t s_width, const color_type background_color ); Row( const size_t s_width, const color_type background_color );
void insert_cell( int col, color_type background_color ); void insert_cell( int col, color_type background_color );
@@ -226,19 +231,17 @@ namespace Terminal {
void reset( color_type background_color ); void reset( color_type background_color );
bool operator==( const Row &x ) const bool operator==( const Row& x ) const { return ( gen == x.gen && cells == x.cells ); }
{
return ( gen == x.gen && cells == x.cells );
}
bool get_wrap( void ) const { return cells.back().get_wrap(); } bool get_wrap( void ) const { return cells.back().get_wrap(); }
void set_wrap( bool w ) { cells.back().set_wrap( w ); } void set_wrap( bool w ) { cells.back().set_wrap( w ); }
uint64_t get_gen() const; uint64_t get_gen() const;
}; };
class SavedCursor { class SavedCursor
public: {
public:
int cursor_col, cursor_row; int cursor_col, cursor_row;
Renditions renditions; Renditions renditions;
/* not implemented: character set shift state */ /* not implemented: character set shift state */
@@ -247,10 +250,11 @@ namespace Terminal {
/* not implemented: state of selective erase */ /* not implemented: state of selective erase */
SavedCursor(); SavedCursor();
}; };
class DrawState { class DrawState
private: {
private:
int width, height; int width, height;
void new_grapheme( void ); void new_grapheme( void );
@@ -270,7 +274,7 @@ namespace Terminal {
SavedCursor save; SavedCursor save;
public: public:
bool next_print_will_wrap; bool next_print_will_wrap;
bool origin_mode; bool origin_mode;
bool auto_wrap_mode; bool auto_wrap_mode;
@@ -279,7 +283,8 @@ namespace Terminal {
bool reverse_video; bool reverse_video;
bool bracketed_paste; bool bracketed_paste;
enum MouseReportingMode { enum MouseReportingMode
{
MOUSE_REPORTING_NONE = 0, MOUSE_REPORTING_NONE = 0,
MOUSE_REPORTING_X10 = 9, MOUSE_REPORTING_X10 = 9,
MOUSE_REPORTING_VT220 = 1000, MOUSE_REPORTING_VT220 = 1000,
@@ -291,7 +296,8 @@ namespace Terminal {
bool mouse_focus_event; // 1004 bool mouse_focus_event; // 1004
bool mouse_alternate_scroll; // 1007 bool mouse_alternate_scroll; // 1007
enum MouseEncodingMode { enum MouseEncodingMode
{
MOUSE_ENCODING_DEFAULT = 0, MOUSE_ENCODING_DEFAULT = 0,
MOUSE_ENCODING_UTF8 = 1005, MOUSE_ENCODING_UTF8 = 1005,
MOUSE_ENCODING_SGR = 1006, MOUSE_ENCODING_SGR = 1006,
@@ -341,19 +347,20 @@ namespace Terminal {
DrawState( int s_width, int s_height ); DrawState( int s_width, int s_height );
bool operator==( const DrawState &x ) const bool operator==( const DrawState& x ) const
{ {
/* only compare fields that affect display */ /* only compare fields that affect display */
return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col ) return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) && && ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible )
( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) && && ( reverse_video == x.reverse_video ) && ( renditions == x.renditions )
( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode ) && && ( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode )
( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll) && && ( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll )
( mouse_encoding_mode == x.mouse_encoding_mode ); && ( mouse_encoding_mode == x.mouse_encoding_mode );
} }
}; };
class Framebuffer { class Framebuffer
{
// To minimize copying of rows and cells, we use shared_ptr to // To minimize copying of rows and cells, we use shared_ptr to
// share unchanged rows between multiple Framebuffers. If we // share unchanged rows between multiple Framebuffers. If we
// write to a row in a Framebuffer and it is shared with other // write to a row in a Framebuffer and it is shared with other
@@ -366,12 +373,12 @@ namespace Terminal {
// Framebuffers is to simply compare the pointer values. If they // Framebuffers is to simply compare the pointer values. If they
// are equal, then the rows are obviously identical. // are equal, then the rows are obviously identical.
// * If no row is shared, the frame has not been modified. // * If no row is shared, the frame has not been modified.
public: public:
typedef std::vector<wchar_t> title_type; typedef std::vector<wchar_t> title_type;
typedef std::shared_ptr<Row> row_pointer; typedef std::shared_ptr<Row> row_pointer;
typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */ typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */
private: private:
rows_type rows; rows_type rows;
title_type icon_name; title_type icon_name;
title_type window_title; title_type window_title;
@@ -386,54 +393,60 @@ namespace Terminal {
return std::make_shared<Row>( w, c ); return std::make_shared<Row>( w, c );
} }
public: public:
Framebuffer( int s_width, int s_height ); Framebuffer( int s_width, int s_height );
Framebuffer( const Framebuffer &other ); Framebuffer( const Framebuffer& other );
Framebuffer &operator=( const Framebuffer &other ); Framebuffer& operator=( const Framebuffer& other );
DrawState ds; DrawState ds;
const rows_type &get_rows() const { return rows; } const rows_type& get_rows() const { return rows; }
void scroll( int N ); void scroll( int N );
void move_rows_autoscroll( int rows ); void move_rows_autoscroll( int rows );
inline const Row *get_row( int row ) const inline const Row* get_row( int row ) const
{ {
if ( row == -1 ) row = ds.get_cursor_row(); if ( row == -1 )
row = ds.get_cursor_row();
return rows.at( row ).get(); return rows.at( row ).get();
} }
inline const Cell *get_cell( int row = -1, int col = -1 ) const inline const Cell* get_cell( int row = -1, int col = -1 ) const
{ {
if ( row == -1 ) row = ds.get_cursor_row(); if ( row == -1 )
if ( col == -1 ) col = ds.get_cursor_col(); row = ds.get_cursor_row();
if ( col == -1 )
col = ds.get_cursor_col();
return &rows.at( row )->cells.at( col ); return &rows.at( row )->cells.at( col );
} }
Row *get_mutable_row( int row ) Row* get_mutable_row( int row )
{ {
if ( row == -1 ) row = ds.get_cursor_row(); if ( row == -1 )
row_pointer &mutable_row = rows.at( row ); row = ds.get_cursor_row();
row_pointer& mutable_row = rows.at( row );
// If the row is shared, copy it. // If the row is shared, copy it.
if (!mutable_row.unique()) { if ( !mutable_row.unique() ) {
mutable_row = std::make_shared<Row>( *mutable_row ); mutable_row = std::make_shared<Row>( *mutable_row );
} }
return mutable_row.get(); return mutable_row.get();
} }
Cell *get_mutable_cell( int row = -1, int col = -1 ) Cell* get_mutable_cell( int row = -1, int col = -1 )
{ {
if ( row == -1 ) row = ds.get_cursor_row(); if ( row == -1 )
if ( col == -1 ) col = ds.get_cursor_col(); row = ds.get_cursor_row();
if ( col == -1 )
col = ds.get_cursor_col();
return &get_mutable_row( row )->cells.at( col ); return &get_mutable_row( row )->cells.at( col );
} }
Cell *get_combining_cell( void ); Cell* get_combining_cell( void );
void apply_renditions_to_cell( Cell *cell ); void apply_renditions_to_cell( Cell* cell );
void insert_line( int before_row, int count ); void insert_line( int before_row, int count );
void delete_line( int row, int count ); void delete_line( int row, int count );
@@ -446,28 +459,29 @@ namespace Terminal {
void set_title_initialized( void ) { title_initialized = true; } void set_title_initialized( void ) { title_initialized = true; }
bool is_title_initialized( void ) const { return title_initialized; } bool is_title_initialized( void ) const { return title_initialized; }
void set_icon_name( const title_type &s ) { icon_name = s; } void set_icon_name( const title_type& s ) { icon_name = s; }
void set_window_title( const title_type &s ) { window_title = s; } void set_window_title( const title_type& s ) { window_title = s; }
void set_clipboard( const title_type &s ) { clipboard = s; } void set_clipboard( const title_type& s ) { clipboard = s; }
const title_type & get_icon_name( void ) const { return icon_name; } const title_type& get_icon_name( void ) const { return icon_name; }
const title_type & get_window_title( void ) const { return window_title; } const title_type& get_window_title( void ) const { return window_title; }
const title_type & get_clipboard( void ) const { return clipboard; } const title_type& get_clipboard( void ) const { return clipboard; }
void prefix_window_title( const title_type &s ); void prefix_window_title( const title_type& s );
void resize( int s_width, int s_height ); void resize( int s_width, int s_height );
void reset_cell( Cell *c ) { c->reset( ds.get_background_rendition() ); } void reset_cell( Cell* c ) { c->reset( ds.get_background_rendition() ); }
void reset_row( Row *r ) { r->reset( ds.get_background_rendition() ); } void reset_row( Row* r ) { r->reset( ds.get_background_rendition() ); }
void ring_bell( void ) { bell_count++; } void ring_bell( void ) { bell_count++; }
unsigned int get_bell_count( void ) const { return bell_count; } unsigned int get_bell_count( void ) const { return bell_count; }
bool operator==( const Framebuffer &x ) const bool operator==( const Framebuffer& x ) const
{ {
return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard ) && ( bell_count == x.bell_count ) && ( ds == x.ds ); return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard )
&& ( bell_count == x.bell_count ) && ( ds == x.ds );
} }
}; };
} }
#endif #endif
+95 -94
View File
@@ -36,15 +36,15 @@
#include <unistd.h> #include <unistd.h>
#include "terminaldispatcher.h"
#include "src/terminal/terminalframebuffer.h"
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h"
using namespace Terminal; using namespace Terminal;
/* Terminal functions -- routines activated by CSI, escape or a control char */ /* Terminal functions -- routines activated by CSI, escape or a control char */
static void clearline( Framebuffer *fb, int row, int start, int end ) static void clearline( Framebuffer* fb, int row, int start, int end )
{ {
for ( int col = start; col <= end; col++ ) { for ( int col = start; col <= end; col++ ) {
fb->reset_cell( fb->get_mutable_cell( row, col ) ); fb->reset_cell( fb->get_mutable_cell( row, col ) );
@@ -52,7 +52,7 @@ static void clearline( Framebuffer *fb, int row, int start, int end )
} }
/* erase in line */ /* erase in line */
static void CSI_EL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_EL( Framebuffer* fb, Dispatcher* dispatch )
{ {
switch ( dispatch->getparam( 0, 0 ) ) { switch ( dispatch->getparam( 0, 0 ) ) {
case 0: /* default: active position to end of line, inclusive */ case 0: /* default: active position to end of line, inclusive */
@@ -72,7 +72,8 @@ static void CSI_EL( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_EL( CSI, "K", CSI_EL ); static Function func_CSI_EL( CSI, "K", CSI_EL );
/* erase in display */ /* erase in display */
static void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) { static void CSI_ED( Framebuffer* fb, Dispatcher* dispatch )
{
switch ( dispatch->getparam( 0, 0 ) ) { switch ( dispatch->getparam( 0, 0 ) ) {
case 0: /* active position to end of screen, inclusive */ case 0: /* active position to end of screen, inclusive */
clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 );
@@ -99,11 +100,11 @@ static void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) {
static Function func_CSI_ED( CSI, "J", CSI_ED ); static Function func_CSI_ED( CSI, "J", CSI_ED );
/* cursor movement -- relative and absolute */ /* cursor movement -- relative and absolute */
static void CSI_cursormove( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_cursormove( Framebuffer* fb, Dispatcher* dispatch )
{ {
int num = dispatch->getparam( 0, 1 ); int num = dispatch->getparam( 0, 1 );
switch ( dispatch->get_dispatch_chars()[ 0 ] ) { switch ( dispatch->get_dispatch_chars()[0] ) {
case 'A': case 'A':
fb->ds.move_row( -num, true ); fb->ds.move_row( -num, true );
break; break;
@@ -134,7 +135,7 @@ static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove );
static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove );
/* device attributes */ /* device attributes */
static void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) static void CSI_DA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch )
{ {
dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */ dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */
} }
@@ -142,7 +143,7 @@ static void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch
static Function func_CSI_DA( CSI, "c", CSI_DA ); static Function func_CSI_DA( CSI, "c", CSI_DA );
/* secondary device attributes */ /* secondary device attributes */
static void CSI_SDA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) static void CSI_SDA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch )
{ {
dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */ dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */
} }
@@ -150,7 +151,7 @@ static void CSI_SDA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch
static Function func_CSI_SDA( CSI, ">c", CSI_SDA ); static Function func_CSI_SDA( CSI, ">c", CSI_SDA );
/* screen alignment diagnostic */ /* screen alignment diagnostic */
static void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECALN( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) {
for ( int x = 0; x < fb->ds.get_width(); x++ ) { for ( int x = 0; x < fb->ds.get_width(); x++ ) {
@@ -163,7 +164,7 @@ static void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unuse
static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN ); static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN );
/* line feed */ /* line feed */
static void Ctrl_LF( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_LF( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->move_rows_autoscroll( 1 ); fb->move_rows_autoscroll( 1 );
} }
@@ -175,7 +176,7 @@ static Function func_Ctrl_VT( CONTROL, "\x0b", Ctrl_LF );
static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF ); static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF );
/* carriage return */ /* carriage return */
static void Ctrl_CR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_CR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
} }
@@ -183,7 +184,7 @@ static void Ctrl_CR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR ); static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR );
/* backspace */ /* backspace */
static void Ctrl_BS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_BS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( -1, true ); fb->ds.move_col( -1, true );
} }
@@ -191,7 +192,7 @@ static void Ctrl_BS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS ); static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS );
/* reverse index -- like a backwards line feed */ /* reverse index -- like a backwards line feed */
static void Ctrl_RI( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_RI( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->move_rows_autoscroll( -1 ); fb->move_rows_autoscroll( -1 );
} }
@@ -199,7 +200,7 @@ static void Ctrl_RI( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI ); static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI );
/* newline */ /* newline */
static void Ctrl_NEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_NEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
fb->move_rows_autoscroll( 1 ); fb->move_rows_autoscroll( 1 );
@@ -208,7 +209,7 @@ static void Ctrl_NEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)
static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL ); static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL );
/* horizontal tab */ /* horizontal tab */
static void HT_n( Framebuffer *fb, size_t count ) static void HT_n( Framebuffer* fb, size_t count )
{ {
int col = fb->ds.get_next_tab( count ); int col = fb->ds.get_next_tab( count );
if ( col == -1 ) { /* no tabs, go to end of line */ if ( col == -1 ) { /* no tabs, go to end of line */
@@ -223,16 +224,16 @@ static void HT_n( Framebuffer *fb, size_t count )
fb->ds.next_print_will_wrap = wrap_state_save; fb->ds.next_print_will_wrap = wrap_state_save;
} }
static void Ctrl_HT( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_HT( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
HT_n( fb, 1 ); HT_n( fb, 1 );
} }
static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false ); static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false );
static void CSI_CxT( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_CxT( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 1 ); int param = dispatch->getparam( 0, 1 );
if ( dispatch->get_dispatch_chars()[ 0 ] == 'Z' ) { if ( dispatch->get_dispatch_chars()[0] == 'Z' ) {
param = -param; param = -param;
} }
if ( param == 0 ) { if ( param == 0 ) {
@@ -245,7 +246,7 @@ static Function func_CSI_CHT( CSI, "I", CSI_CxT, false );
static Function func_CSI_CBT( CSI, "Z", CSI_CxT, false ); static Function func_CSI_CBT( CSI, "Z", CSI_CxT, false );
/* horizontal tab set */ /* horizontal tab set */
static void Ctrl_HTS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_HTS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.set_tab(); fb->ds.set_tab();
} }
@@ -253,7 +254,7 @@ static void Ctrl_HTS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)
static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS ); static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS );
/* tabulation clear */ /* tabulation clear */
static void CSI_TBC( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_TBC( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 0 ); int param = dispatch->getparam( 0, 0 );
switch ( param ) { switch ( param ) {
@@ -274,10 +275,11 @@ static void CSI_TBC( Framebuffer *fb, Dispatcher *dispatch )
/* TBC preserves wrap state */ /* TBC preserves wrap state */
static Function func_CSI_TBC( CSI, "g", CSI_TBC, false ); static Function func_CSI_TBC( CSI, "g", CSI_TBC, false );
static bool *get_DEC_mode( int param, Framebuffer *fb ) { static bool* get_DEC_mode( int param, Framebuffer* fb )
{
switch ( param ) { switch ( param ) {
case 1: /* cursor key mode */ case 1: /* cursor key mode */
return &(fb->ds.application_mode_cursor_keys); return &( fb->ds.application_mode_cursor_keys );
case 3: /* 80/132. Ignore but clear screen. */ case 3: /* 80/132. Ignore but clear screen. */
/* clear screen */ /* clear screen */
fb->ds.move_row( 0 ); fb->ds.move_row( 0 );
@@ -287,21 +289,21 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
} }
return NULL; return NULL;
case 5: /* reverse video */ case 5: /* reverse video */
return &(fb->ds.reverse_video); return &( fb->ds.reverse_video );
case 6: /* origin */ case 6: /* origin */
fb->ds.move_row( 0 ); fb->ds.move_row( 0 );
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
return &(fb->ds.origin_mode); return &( fb->ds.origin_mode );
case 7: /* auto wrap */ case 7: /* auto wrap */
return &(fb->ds.auto_wrap_mode); return &( fb->ds.auto_wrap_mode );
case 25: case 25:
return &(fb->ds.cursor_visible); return &( fb->ds.cursor_visible );
case 1004: /* xterm mouse focus event */ case 1004: /* xterm mouse focus event */
return &(fb->ds.mouse_focus_event); return &( fb->ds.mouse_focus_event );
case 1007: /* xterm mouse alternate scroll */ case 1007: /* xterm mouse alternate scroll */
return &(fb->ds.mouse_alternate_scroll); return &( fb->ds.mouse_alternate_scroll );
case 2004: /* bracketed paste */ case 2004: /* bracketed paste */
return &(fb->ds.bracketed_paste); return &( fb->ds.bracketed_paste );
default: default:
break; break;
} }
@@ -309,20 +311,22 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
} }
/* helper for CSI_DECSM and CSI_DECRM */ /* helper for CSI_DECSM and CSI_DECRM */
static void set_if_available( bool *mode, bool value ) static void set_if_available( bool* mode, bool value )
{ {
if ( mode ) { *mode = value; } if ( mode ) {
*mode = value;
}
} }
/* set private mode */ /* set private mode */
static void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECSM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int param = dispatch->getparam( i, 0 ); int param = dispatch->getparam( i, 0 );
if (param == 9 || (param >= 1000 && param <= 1003)) { if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) {
fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode) param; fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode)param;
} else if (param == 1005 || param == 1006 || param == 1015) { } else if ( param == 1005 || param == 1006 || param == 1015 ) {
fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode) param; fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode)param;
} else { } else {
set_if_available( get_DEC_mode( param, fb ), true ); set_if_available( get_DEC_mode( param, fb ), true );
} }
@@ -330,13 +334,13 @@ static void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
} }
/* clear private mode */ /* clear private mode */
static void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECRM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int param = dispatch->getparam( i, 0 ); int param = dispatch->getparam( i, 0 );
if (param == 9 || (param >= 1000 && param <= 1003)) { if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) {
fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE; fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE;
} else if (param == 1005 || param == 1006 || param == 1015) { } else if ( param == 1005 || param == 1006 || param == 1015 ) {
fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT; fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT;
} else { } else {
set_if_available( get_DEC_mode( param, fb ), false ); set_if_available( get_DEC_mode( param, fb ), false );
@@ -348,18 +352,19 @@ static void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false ); static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false );
static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false ); static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false );
static bool *get_ANSI_mode( int param, Framebuffer *fb ) { static bool* get_ANSI_mode( int param, Framebuffer* fb )
{
if ( param == 4 ) { /* insert/replace mode */ if ( param == 4 ) { /* insert/replace mode */
return &(fb->ds.insert_mode); return &( fb->ds.insert_mode );
} }
return NULL; return NULL;
} }
/* set mode */ /* set mode */
static void CSI_SM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb );
if ( mode ) { if ( mode ) {
*mode = true; *mode = true;
} }
@@ -367,10 +372,10 @@ static void CSI_SM( Framebuffer *fb, Dispatcher *dispatch )
} }
/* clear mode */ /* clear mode */
static void CSI_RM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_RM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb );
if ( mode ) { if ( mode ) {
*mode = false; *mode = false;
} }
@@ -381,14 +386,12 @@ static Function func_CSI_SM( CSI, "h", CSI_SM );
static Function func_CSI_RM( CSI, "l", CSI_RM ); static Function func_CSI_RM( CSI, "l", CSI_RM );
/* set top and bottom margins */ /* set top and bottom margins */
static void CSI_DECSTBM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECSTBM( Framebuffer* fb, Dispatcher* dispatch )
{ {
int top = dispatch->getparam( 0, 1 ); int top = dispatch->getparam( 0, 1 );
int bottom = dispatch->getparam( 1, fb->ds.get_height() ); int bottom = dispatch->getparam( 1, fb->ds.get_height() );
if ( (bottom <= top) if ( ( bottom <= top ) || ( top > fb->ds.get_height() ) || ( top == 0 && bottom == 1 ) ) {
|| (top > fb->ds.get_height())
|| (top == 0 && bottom == 1) ) {
return; /* invalid, xterm ignores */ return; /* invalid, xterm ignores */
} }
@@ -400,14 +403,15 @@ static void CSI_DECSTBM( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM ); static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM );
/* terminal bell */ /* terminal bell */
static void Ctrl_BEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { static void Ctrl_BEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{
fb->ring_bell(); fb->ring_bell();
} }
static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL ); static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL );
/* select graphics rendition -- e.g., bold, blinking, etc. */ /* select graphics rendition -- e.g., bold, blinking, etc. */
static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SGR( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int rendition = dispatch->getparam( i, 0 ); int rendition = dispatch->getparam( i, 0 );
@@ -415,23 +419,20 @@ static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch )
because Ps of 0 in that case does not mean reset to default, even because Ps of 0 in that case does not mean reset to default, even
though it means that otherwise (as usually renditions are applied though it means that otherwise (as usually renditions are applied
in order). */ in order). */
if ((rendition == 38 || rendition == 48) && if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 3 )
(dispatch->param_count() - i >= 3) && && ( dispatch->getparam( i + 1, -1 ) == 5 ) ) {
(dispatch->getparam( i+1, -1 ) == 5)) { ( rendition == 38 ) ? fb->ds.set_foreground_color( dispatch->getparam( i + 2, 0 ) )
(rendition == 38) ? : fb->ds.set_background_color( dispatch->getparam( i + 2, 0 ) );
fb->ds.set_foreground_color( dispatch->getparam( i+2, 0 ) ) :
fb->ds.set_background_color( dispatch->getparam( i+2, 0 ) );
i += 2; i += 2;
continue; continue;
} }
/* True color support: ESC[ ... [34]8;2;<r>;<g>;<b> ... m */ /* True color support: ESC[ ... [34]8;2;<r>;<g>;<b> ... m */
if ( (rendition == 38 || rendition == 48) && if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 5 )
(dispatch->param_count() - i >= 5) && && ( dispatch->getparam( i + 1, -1 ) == 2 ) ) {
(dispatch->getparam( i+1, -1 ) == 2)) { unsigned int red = dispatch->getparam( i + 2, 0 );
unsigned int red = dispatch->getparam(i+2, 0); unsigned int green = dispatch->getparam( i + 3, 0 );
unsigned int green = dispatch->getparam(i+3, 0); unsigned int blue = dispatch->getparam( i + 4, 0 );
unsigned int blue = dispatch->getparam(i+4, 0);
unsigned int color; unsigned int color;
color = Renditions::make_true_color( red, green, blue ); color = Renditions::make_true_color( red, green, blue );
@@ -452,12 +453,12 @@ static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */ static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */
/* save and restore cursor */ /* save and restore cursor */
static void Esc_DECSC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECSC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.save_cursor(); fb->ds.save_cursor();
} }
static void Esc_DECRC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECRC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.restore_cursor(); fb->ds.restore_cursor();
} }
@@ -466,7 +467,7 @@ static Function func_Esc_DECSC( ESCAPE, "7", Esc_DECSC );
static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC ); static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC );
/* device status report -- e.g., cursor position (used by resize) */ /* device status report -- e.g., cursor position (used by resize) */
static void CSI_DSR( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DSR( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 0 ); int param = dispatch->getparam( 0, 0 );
@@ -475,10 +476,8 @@ static void CSI_DSR( Framebuffer *fb, Dispatcher *dispatch )
dispatch->terminal_to_host.append( "\033[0n" ); dispatch->terminal_to_host.append( "\033[0n" );
break; break;
case 6: /* report of active position requested */ case 6: /* report of active position requested */
char cpr[ 32 ]; char cpr[32];
snprintf( cpr, 32, "\033[%d;%dR", snprintf( cpr, 32, "\033[%d;%dR", fb->ds.get_cursor_row() + 1, fb->ds.get_cursor_col() + 1 );
fb->ds.get_cursor_row() + 1,
fb->ds.get_cursor_col() + 1 );
dispatch->terminal_to_host.append( cpr ); dispatch->terminal_to_host.append( cpr );
break; break;
default: default:
@@ -489,7 +488,7 @@ static void CSI_DSR( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DSR( CSI, "n", CSI_DSR ); static Function func_CSI_DSR( CSI, "n", CSI_DSR );
/* insert line */ /* insert line */
static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_IL( Framebuffer* fb, Dispatcher* dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
@@ -503,7 +502,7 @@ static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_IL( CSI, "L", CSI_IL ); static Function func_CSI_IL( CSI, "L", CSI_IL );
/* delete line */ /* delete line */
static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DL( Framebuffer* fb, Dispatcher* dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
@@ -517,7 +516,7 @@ static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DL( CSI, "M", CSI_DL ); static Function func_CSI_DL( CSI, "M", CSI_DL );
/* insert characters */ /* insert characters */
static void CSI_ICH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_ICH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int cells = dispatch->getparam( 0, 1 ); int cells = dispatch->getparam( 0, 1 );
@@ -529,7 +528,7 @@ static void CSI_ICH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_ICH( CSI, "@", CSI_ICH ); static Function func_CSI_ICH( CSI, "@", CSI_ICH );
/* delete character */ /* delete character */
static void CSI_DCH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DCH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int cells = dispatch->getparam( 0, 1 ); int cells = dispatch->getparam( 0, 1 );
@@ -541,7 +540,7 @@ static void CSI_DCH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DCH( CSI, "P", CSI_DCH ); static Function func_CSI_DCH( CSI, "P", CSI_DCH );
/* line position absolute */ /* line position absolute */
static void CSI_VPA( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_VPA( Framebuffer* fb, Dispatcher* dispatch )
{ {
int row = dispatch->getparam( 0, 1 ); int row = dispatch->getparam( 0, 1 );
fb->ds.move_row( row - 1 ); fb->ds.move_row( row - 1 );
@@ -550,7 +549,7 @@ static void CSI_VPA( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_VPA( CSI, "d", CSI_VPA ); static Function func_CSI_VPA( CSI, "d", CSI_VPA );
/* character position absolute */ /* character position absolute */
static void CSI_HPA( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_HPA( Framebuffer* fb, Dispatcher* dispatch )
{ {
int col = dispatch->getparam( 0, 1 ); int col = dispatch->getparam( 0, 1 );
fb->ds.move_col( col - 1 ); fb->ds.move_col( col - 1 );
@@ -560,7 +559,7 @@ static Function func_CSI_CHA( CSI, "G", CSI_HPA ); /* ECMA-48 name: CHA */
static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */ static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */
/* erase character */ /* erase character */
static void CSI_ECH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_ECH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int num = dispatch->getparam( 0, 1 ); int num = dispatch->getparam( 0, 1 );
int limit = fb->ds.get_cursor_col() + num - 1; int limit = fb->ds.get_cursor_col() + num - 1;
@@ -574,7 +573,7 @@ static void CSI_ECH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_ECH( CSI, "X", CSI_ECH ); static Function func_CSI_ECH( CSI, "X", CSI_ECH );
/* reset to initial state */ /* reset to initial state */
static void Esc_RIS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_RIS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->reset(); fb->reset();
} }
@@ -582,7 +581,7 @@ static void Esc_RIS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS ); static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS );
/* soft reset */ /* soft reset */
static void CSI_DECSTR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void CSI_DECSTR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->soft_reset(); fb->soft_reset();
} }
@@ -590,45 +589,47 @@ static void CSI_DECSTR( Framebuffer *fb, Dispatcher *dispatch __attribute((unuse
static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR ); static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR );
/* xterm uses an Operating System Command to set the window title */ /* xterm uses an Operating System Command to set the window title */
void Dispatcher::OSC_dispatch( const Parser::OSC_End *act __attribute((unused)), Framebuffer *fb ) void Dispatcher::OSC_dispatch( const Parser::OSC_End* act __attribute( ( unused ) ), Framebuffer* fb )
{ {
/* handle osc copy clipboard sequence 52;c; */ /* handle osc copy clipboard sequence 52;c; */
if ( OSC_string.size() >= 5 && OSC_string[ 0 ] == L'5' && if ( OSC_string.size() >= 5 && OSC_string[0] == L'5' && OSC_string[1] == L'2' && OSC_string[2] == L';'
OSC_string[ 1 ] == L'2' && OSC_string[ 2 ] == L';' && && OSC_string[3] == L'c' && OSC_string[4] == L';' ) {
OSC_string[ 3 ] == L'c' && OSC_string[ 4 ] == L';') { Terminal::Framebuffer::title_type clipboard( OSC_string.begin() + 5, OSC_string.end() );
Terminal::Framebuffer::title_type clipboard(
OSC_string.begin() + 5, OSC_string.end() );
fb->set_clipboard( clipboard ); fb->set_clipboard( clipboard );
/* handle osc terminal title sequence */ /* handle osc terminal title sequence */
} else if ( OSC_string.size() >= 1 ) { } else if ( OSC_string.size() >= 1 ) {
long cmd_num = -1; long cmd_num = -1;
int offset = 0; int offset = 0;
if ( OSC_string[ 0 ] == L';' ) { if ( OSC_string[0] == L';' ) {
/* OSC of the form "\033];<title>\007" */ /* OSC of the form "\033];<title>\007" */
cmd_num = 0; /* treat it as as a zero */ cmd_num = 0; /* treat it as as a zero */
offset = 1; offset = 1;
} else if ( (OSC_string.size() >= 2) && (OSC_string[ 1 ] == L';') ) { } else if ( ( OSC_string.size() >= 2 ) && ( OSC_string[1] == L';' ) ) {
/* OSC of the form "\033]X;<title>\007" where X can be: /* OSC of the form "\033]X;<title>\007" where X can be:
* 0: set icon name and window title * 0: set icon name and window title
* 1: set icon name * 1: set icon name
* 2: set window title */ * 2: set window title */
cmd_num = OSC_string[ 0 ] - L'0'; cmd_num = OSC_string[0] - L'0';
offset = 2; offset = 2;
} }
bool set_icon = cmd_num == 0 || cmd_num == 1; bool set_icon = cmd_num == 0 || cmd_num == 1;
bool set_title = cmd_num == 0 || cmd_num == 2; bool set_title = cmd_num == 0 || cmd_num == 2;
if ( set_icon || set_title ) { if ( set_icon || set_title ) {
fb->set_title_initialized(); fb->set_title_initialized();
int title_length = std::min(OSC_string.size(), (size_t)256); int title_length = std::min( OSC_string.size(), (size_t)256 );
Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.begin() + title_length ); Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.begin() + title_length );
if ( set_icon ) { fb->set_icon_name( newtitle ); } if ( set_icon ) {
if ( set_title ) { fb->set_window_title( newtitle ); } fb->set_icon_name( newtitle );
}
if ( set_title ) {
fb->set_window_title( newtitle );
}
} }
} }
} }
/* scroll down or terminfo indn */ /* scroll down or terminfo indn */
static void CSI_SD( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SD( Framebuffer* fb, Dispatcher* dispatch )
{ {
fb->scroll( dispatch->getparam( 0, 1 ) ); fb->scroll( dispatch->getparam( 0, 1 ) );
} }
@@ -636,7 +637,7 @@ static void CSI_SD( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_SD( CSI, "S", CSI_SD ); static Function func_CSI_SD( CSI, "S", CSI_SD );
/* scroll up or terminfo rin */ /* scroll up or terminfo rin */
static void CSI_SU( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SU( Framebuffer* fb, Dispatcher* dispatch )
{ {
fb->scroll( -dispatch->getparam( 0, 1 ) ); fb->scroll( -dispatch->getparam( 0, 1 ) );
} }
+4 -7
View File
@@ -36,8 +36,7 @@
using namespace Terminal; using namespace Terminal;
std::string UserInput::input( const Parser::UserByte *act, std::string UserInput::input( const Parser::UserByte* act, bool application_mode_cursor_keys )
bool application_mode_cursor_keys )
{ {
/* The user will always be in application mode. If stm is not in /* The user will always be in application mode. If stm is not in
application mode, convert user's cursor control function to an application mode, convert user's cursor control function to an
@@ -66,13 +65,11 @@ std::string UserInput::input( const Parser::UserByte *act,
case SS3: case SS3:
state = Ground; state = Ground;
if ( (!application_mode_cursor_keys) if ( ( !application_mode_cursor_keys ) && ( act->c >= 'A' ) && ( act->c <= 'D' ) ) {
&& (act->c >= 'A') char translated_cursor[2] = { '[', act->c };
&& (act->c <= 'D') ) {
char translated_cursor[ 2 ] = { '[', act->c };
return std::string( translated_cursor, 2 ); return std::string( translated_cursor, 2 );
} else { } else {
char original_cursor[ 2 ] = { 'O', act->c }; char original_cursor[2] = { 'O', act->c };
return std::string( original_cursor, 2 ); return std::string( original_cursor, 2 );
} }
+12 -13
View File
@@ -33,31 +33,30 @@
#ifndef TERMINALUSERINPUT_HPP #ifndef TERMINALUSERINPUT_HPP
#define TERMINALUSERINPUT_HPP #define TERMINALUSERINPUT_HPP
#include <string>
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include <string>
namespace Terminal { namespace Terminal {
class UserInput { class UserInput
public: {
enum UserInputState { public:
enum UserInputState
{
Ground, Ground,
ESC, ESC,
SS3 SS3
}; };
private: private:
UserInputState state; UserInputState state;
public: public:
UserInput() UserInput() : state( Ground ) {}
: state( Ground )
{}
std::string input( const Parser::UserByte *act, std::string input( const Parser::UserByte* act, bool application_mode_cursor_keys );
bool application_mode_cursor_keys );
bool operator==( const UserInput &x ) const { return state == x.state; } bool operator==( const UserInput& x ) const { return state == x.state; }
}; };
} }
#endif #endif
+25 -24
View File
@@ -38,11 +38,11 @@
the Mosh project. */ the Mosh project. */
#include <cstdint> #include <cstdint>
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include "src/crypto/base64.h"
#include "base64_vector.h" #include "base64_vector.h"
#include "src/crypto/base64.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/crypto/prng.h" #include "src/crypto/prng.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
@@ -54,38 +54,39 @@
bool verbose = false; bool verbose = false;
static void test_base64( void ) { static void test_base64( void )
{
/* run through a test vector */ /* run through a test vector */
char encoded[25]; char encoded[25];
uint8_t decoded[16]; uint8_t decoded[16];
size_t b64_len = 24; size_t b64_len = 24;
size_t raw_len = 16; size_t raw_len = 16;
for ( base64_test_row *row = static_base64_vector; *row->native != '\0'; row++ ) { for ( base64_test_row* row = static_base64_vector; *row->native != '\0'; row++ ) {
memset(encoded, '\0', sizeof encoded); memset( encoded, '\0', sizeof encoded );
memset(decoded, '\0', sizeof decoded); memset( decoded, '\0', sizeof decoded );
base64_encode(static_cast<const uint8_t *>(row->native), raw_len, encoded, b64_len); base64_encode( static_cast<const uint8_t*>( row->native ), raw_len, encoded, b64_len );
fatal_assert( b64_len == 24 ); fatal_assert( b64_len == 24 );
fatal_assert( !memcmp(row->encoded, encoded, sizeof encoded)); fatal_assert( !memcmp( row->encoded, encoded, sizeof encoded ) );
fatal_assert( base64_decode(row->encoded, b64_len, decoded, &raw_len )); fatal_assert( base64_decode( row->encoded, b64_len, decoded, &raw_len ) );
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
fatal_assert( !memcmp(row->native, decoded, sizeof decoded)); fatal_assert( !memcmp( row->native, decoded, sizeof decoded ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "validation PASSED\n" ); printf( "validation PASSED\n" );
} }
/* try 0..255 in the last byte; make sure the final two characters are output properly */ /* try 0..255 in the last byte; make sure the final two characters are output properly */
uint8_t source[16]; uint8_t source[16];
memset(source, '\0', sizeof source); memset( source, '\0', sizeof source );
for ( int i = 0; i < 256; i++ ) { for ( int i = 0; i < 256; i++ ) {
source[15] = i; source[15] = i;
base64_encode(source, raw_len, encoded, b64_len); base64_encode( source, raw_len, encoded, b64_len );
fatal_assert( b64_len == 24 ); fatal_assert( b64_len == 24 );
fatal_assert( base64_decode(encoded, b64_len, decoded, &raw_len )); fatal_assert( base64_decode( encoded, b64_len, decoded, &raw_len ) );
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
fatal_assert( !memcmp(source, decoded, sizeof decoded)); fatal_assert( !memcmp( source, decoded, sizeof decoded ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "last-byte PASSED\n" ); printf( "last-byte PASSED\n" );
@@ -93,17 +94,17 @@ static void test_base64( void ) {
/* randomly try keys */ /* randomly try keys */
PRNG prng; PRNG prng;
for ( int i = 0; i < ( 1<<17 ); i++ ) { for ( int i = 0; i < ( 1 << 17 ); i++ ) {
Base64Key key1(prng); Base64Key key1( prng );
Base64Key key2(key1.printable_key()); Base64Key key2( key1.printable_key() );
fatal_assert( key1.printable_key() == key2.printable_key() && !memcmp(key1.data(), key2.data(), 16 )); fatal_assert( key1.printable_key() == key2.printable_key() && !memcmp( key1.data(), key2.data(), 16 ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "random PASSED\n" ); printf( "random PASSED\n" );
} }
/* test bad keys */ /* test bad keys */
const char *bad_keys[] = { const char* bad_keys[] = {
"", "",
"AAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAA=", "AAAAAAAAAAAAAAAAAAAAAA=",
@@ -119,25 +120,25 @@ static void test_base64( void ) {
"AAAAAAAAAA==", "AAAAAAAAAA==",
NULL, NULL,
}; };
for ( const char **key = bad_keys; *key != NULL; key++ ) { for ( const char** key = bad_keys; *key != NULL; key++ ) {
b64_len = 24; b64_len = 24;
raw_len = 16; raw_len = 16;
fatal_assert( !base64_decode(*key, b64_len, decoded, &raw_len )); fatal_assert( !base64_decode( *key, b64_len, decoded, &raw_len ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "bad-keys PASSED\n" ); printf( "bad-keys PASSED\n" );
} }
} }
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
try { try {
test_base64(); test_base64();
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
return 1; return 1;
} }
+6 -2
View File
@@ -1,3 +1,7 @@
struct base64_test_row { const unsigned char native[17]; const char encoded[25]; }; struct base64_test_row
typedef base64_test_row *base64_test_vector; {
const unsigned char native[17];
const char encoded[25];
};
typedef base64_test_row* base64_test_vector;
extern base64_test_row static_base64_vector[]; extern base64_test_row static_base64_vector[];
+18 -15
View File
@@ -48,7 +48,7 @@ using namespace Crypto;
PRNG prng; PRNG prng;
const size_t MESSAGE_SIZE_MAX = (2048 - 16); const size_t MESSAGE_SIZE_MAX = ( 2048 - 16 );
const size_t MESSAGES_PER_SESSION = 256; const size_t MESSAGES_PER_SESSION = 256;
const size_t NUM_SESSIONS = 64; const size_t NUM_SESSIONS = 64;
@@ -56,27 +56,29 @@ bool verbose = false;
#define NONCE_FMT "%016" PRIx64 #define NONCE_FMT "%016" PRIx64
static std::string random_payload( void ) { static std::string random_payload( void )
{
const size_t len = prng.uint32() % MESSAGE_SIZE_MAX; const size_t len = prng.uint32() % MESSAGE_SIZE_MAX;
char buf[ MESSAGE_SIZE_MAX ]; char buf[MESSAGE_SIZE_MAX];
prng.fill( buf, len ); prng.fill( buf, len );
std::string payload( buf, len ); std::string payload( buf, len );
return payload; return payload;
} }
static void test_bad_decrypt( Session &decryption_session ) { static void test_bad_decrypt( Session& decryption_session )
{
std::string bad_ct = random_payload(); std::string bad_ct = random_payload();
bool got_exn = false; bool got_exn = false;
try { try {
decryption_session.decrypt( bad_ct ); decryption_session.decrypt( bad_ct );
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
got_exn = true; got_exn = true;
/* The "bad decrypt" exception needs to be non-fatal, otherwise we are /* The "bad decrypt" exception needs to be non-fatal, otherwise we are
vulnerable to an easy DoS. */ vulnerable to an easy DoS. */
fatal_assert( ! e.fatal ); fatal_assert( !e.fatal );
} }
if ( verbose ) { if ( verbose ) {
@@ -86,7 +88,8 @@ static void test_bad_decrypt( Session &decryption_session ) {
} }
/* Generate a single key and initial nonce, then perform some encryptions. */ /* Generate a single key and initial nonce, then perform some encryptions. */
static void test_one_session( void ) { static void test_one_session( void )
{
Base64Key key; Base64Key key;
Session encryption_session( key ); Session encryption_session( key );
Session decryption_session( key ); Session decryption_session( key );
@@ -97,7 +100,7 @@ static void test_one_session( void ) {
hexdump( key.data(), 16, "key" ); hexdump( key.data(), 16, "key" );
} }
for ( size_t i=0; i<MESSAGES_PER_SESSION; i++ ) { for ( size_t i = 0; i < MESSAGES_PER_SESSION; i++ ) {
Nonce nonce( nonce_int ); Nonce nonce( nonce_int );
fatal_assert( nonce.val() == nonce_int ); fatal_assert( nonce.val() == nonce_int );
@@ -123,7 +126,7 @@ static void test_one_session( void ) {
nonce_int++; nonce_int++;
if ( ! ( prng.uint8() % 16 ) ) { if ( !( prng.uint8() % 16 ) ) {
test_bad_decrypt( decryption_session ); test_bad_decrypt( decryption_session );
} }
@@ -133,17 +136,17 @@ static void test_one_session( void ) {
} }
} }
int main( int argc, char *argv[] ) { int main( int argc, char* argv[] )
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { {
if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
for ( size_t i=0; i<NUM_SESSIONS; i++ ) { for ( size_t i = 0; i < NUM_SESSIONS; i++ ) {
try { try {
test_one_session(); test_one_session();
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
fprintf( stderr, "Crypto exception: %s\r\n", fprintf( stderr, "Crypto exception: %s\r\n", e.what() );
e.what() );
return 1; return 1;
} }
} }
+4 -4
View File
@@ -56,9 +56,9 @@
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/swrite.h" #include "src/util/swrite.h"
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if (argc < 2) { if ( argc < 2 ) {
fprintf( stderr, "usage: inpty COMMAND [ARGS...]\n" ); fprintf( stderr, "usage: inpty COMMAND [ARGS...]\n" );
return 1; return 1;
} }
@@ -68,7 +68,7 @@ int main( int argc, char *argv[] )
winsize.ws_col = 80; winsize.ws_col = 80;
winsize.ws_row = 24; winsize.ws_row = 24;
int saved_stderr = dup(STDERR_FILENO); int saved_stderr = dup( STDERR_FILENO );
if ( saved_stderr < 0 ) { if ( saved_stderr < 0 ) {
perror( "dup" ); perror( "dup" );
return 1; return 1;
@@ -101,7 +101,7 @@ int main( int argc, char *argv[] )
} }
while ( 1 ) { while ( 1 ) {
char buf[ 1024 ]; char buf[1024];
ssize_t bytes_read = read( master, buf, sizeof( buf ) ); ssize_t bytes_read = read( master, buf, sizeof( buf ) );
if ( bytes_read == 0 || ( bytes_read < 0 && errno == EIO ) ) { /* EOF */ if ( bytes_read == 0 || ( bytes_read < 0 && errno == EIO ) ) { /* EOF */
break; break;
+1 -1
View File
@@ -34,7 +34,7 @@
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
int main( int argc __attribute__(( unused )), char **argv __attribute__(( unused ))) int main( int argc __attribute__( ( unused ) ), char** argv __attribute__( ( unused ) ) )
{ {
set_native_locale(); set_native_locale();
if ( !is_utf8_locale() ) { if ( !is_utf8_locale() ) {
+1 -1
View File
@@ -32,8 +32,8 @@
/* Tests that the Mosh network layer seems to be using unique nonces */ /* Tests that the Mosh network layer seems to be using unique nonces */
#include <iostream>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <set> #include <set>
#include "src/network/network.h" #include "src/network/network.h"
+205 -181
View File
@@ -56,41 +56,50 @@ using Crypto::AlignedBuffer;
bool verbose = false; bool verbose = false;
static bool equal( const AlignedBuffer &a, const AlignedBuffer &b ) { static bool equal( const AlignedBuffer& a, const AlignedBuffer& b )
return ( a.len() == b.len() ) {
&& !memcmp( a.data(), b.data(), a.len() ); return ( a.len() == b.len() ) && !memcmp( a.data(), b.data(), a.len() );
} }
using AlignedPointer = std::shared_ptr<AlignedBuffer>; using AlignedPointer = std::shared_ptr<AlignedBuffer>;
static AlignedBuffer *get_ctx( const AlignedBuffer &key ) { static AlignedBuffer* get_ctx( const AlignedBuffer& key )
AlignedBuffer *ctx_buf = new AlignedBuffer( ae_ctx_sizeof() ); {
AlignedBuffer* ctx_buf = new AlignedBuffer( ae_ctx_sizeof() );
fatal_assert( ctx_buf ); fatal_assert( ctx_buf );
fatal_assert( AE_SUCCESS == ae_init( (ae_ctx *)ctx_buf->data(), key.data(), key.len(), NONCE_LEN, TAG_LEN ) ); fatal_assert( AE_SUCCESS == ae_init( (ae_ctx*)ctx_buf->data(), key.data(), key.len(), NONCE_LEN, TAG_LEN ) );
return ctx_buf; return ctx_buf;
} }
static void scrap_ctx( AlignedBuffer &ctx_buf ) { static void scrap_ctx( AlignedBuffer& ctx_buf )
fatal_assert( AE_SUCCESS == ae_clear( (ae_ctx *)ctx_buf.data() ) ); {
fatal_assert( AE_SUCCESS == ae_clear( (ae_ctx*)ctx_buf.data() ) );
} }
static void test_encrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, static void test_encrypt( const AlignedBuffer& key,
const AlignedBuffer &plaintext, const AlignedBuffer &assoc, const AlignedBuffer& nonce,
const AlignedBuffer &expected_ciphertext ) { const AlignedBuffer& plaintext,
const AlignedBuffer& assoc,
const AlignedBuffer& expected_ciphertext )
{
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer observed_ciphertext( plaintext.len() + TAG_LEN ); AlignedBuffer observed_ciphertext( plaintext.len() + TAG_LEN );
const int ret = ae_encrypt( ctx, nonce.data(), const int ret = ae_encrypt( ctx,
plaintext.data(), plaintext.len(), nonce.data(),
assoc.data(), assoc.len(), plaintext.data(),
observed_ciphertext.data(), NULL, plaintext.len(),
assoc.data(),
assoc.len(),
observed_ciphertext.data(),
NULL,
AE_FINALIZE ); AE_FINALIZE );
if ( verbose ) { if ( verbose ) {
printf( "ret %d\n", ret ); printf( "ret %d\n", ret );
hexdump(observed_ciphertext, "obs ct" ); hexdump( observed_ciphertext, "obs ct" );
} }
fatal_assert( ret == int( expected_ciphertext.len() ) ); fatal_assert( ret == int( expected_ciphertext.len() ) );
@@ -99,19 +108,26 @@ static void test_encrypt( const AlignedBuffer &key, const AlignedBuffer &nonce,
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
static void test_decrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, static void test_decrypt( const AlignedBuffer& key,
const AlignedBuffer &ciphertext, const AlignedBuffer &assoc, const AlignedBuffer& nonce,
const AlignedBuffer &expected_plaintext, const AlignedBuffer& ciphertext,
bool valid ) { const AlignedBuffer& assoc,
const AlignedBuffer& expected_plaintext,
bool valid )
{
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer observed_plaintext( ciphertext.len() - TAG_LEN ); AlignedBuffer observed_plaintext( ciphertext.len() - TAG_LEN );
const int ret = ae_decrypt( ctx, nonce.data(), const int ret = ae_decrypt( ctx,
ciphertext.data(), ciphertext.len(), nonce.data(),
assoc.data(), assoc.len(), ciphertext.data(),
observed_plaintext.data(), NULL, ciphertext.len(),
assoc.data(),
assoc.len(),
observed_plaintext.data(),
NULL,
AE_FINALIZE ); AE_FINALIZE );
if ( verbose ) { if ( verbose ) {
@@ -131,15 +147,20 @@ static void test_decrypt( const AlignedBuffer &key, const AlignedBuffer &nonce,
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
static void test_vector( const char *key_p, const char *nonce_p, static void test_vector( const char* key_p,
size_t assoc_len, const char *assoc_p, const char* nonce_p,
size_t plaintext_len, const char *plaintext_p, size_t assoc_len,
size_t ciphertext_len, const char *ciphertext_p ) { const char* assoc_p,
size_t plaintext_len,
const char* plaintext_p,
size_t ciphertext_len,
const char* ciphertext_p )
{
AlignedBuffer key ( KEY_LEN, key_p ); AlignedBuffer key( KEY_LEN, key_p );
AlignedBuffer nonce ( NONCE_LEN, nonce_p ); AlignedBuffer nonce( NONCE_LEN, nonce_p );
AlignedBuffer plaintext ( plaintext_len, plaintext_p ); AlignedBuffer plaintext( plaintext_len, plaintext_p );
AlignedBuffer assoc ( assoc_len, assoc_p ); AlignedBuffer assoc( assoc_len, assoc_p );
AlignedBuffer ciphertext( ciphertext_len, ciphertext_p ); AlignedBuffer ciphertext( ciphertext_len, ciphertext_p );
if ( verbose ) { if ( verbose ) {
@@ -150,182 +171,198 @@ static void test_vector( const char *key_p, const char *nonce_p,
hexdump( ciphertext, "exp ct" ); hexdump( ciphertext, "exp ct" );
} }
test_encrypt( key, nonce, plaintext, assoc, test_encrypt( key, nonce, plaintext, assoc, ciphertext );
ciphertext );
test_decrypt( key, nonce, ciphertext, assoc, test_decrypt( key, nonce, ciphertext, assoc, plaintext, true );
plaintext, true );
/* Try some bad ciphertexts and make sure they don't validate. */ /* Try some bad ciphertexts and make sure they don't validate. */
PRNG prng; PRNG prng;
for ( size_t i=0; i<64; i++ ) { for ( size_t i = 0; i < 64; i++ ) {
AlignedBuffer bad_ct( ciphertext.len(), ciphertext.data() ); AlignedBuffer bad_ct( ciphertext.len(), ciphertext.data() );
( (uint8_t *) bad_ct.data() )[ prng.uint32() % bad_ct.len() ] ( (uint8_t*)bad_ct.data() )[prng.uint32() % bad_ct.len()] ^= ( 1 << ( prng.uint8() % 8 ) );
^= ( 1 << ( prng.uint8() % 8 ) ); test_decrypt( key, nonce, bad_ct, assoc, plaintext, false );
test_decrypt( key, nonce, bad_ct, assoc,
plaintext, false );
} }
if (verbose) { if ( verbose ) {
printf( "PASSED\n\n" ); printf( "PASSED\n\n" );
} }
} }
#define TEST_VECTOR( _key, _nonce, _assoc, _pt, _ct ) \ #define TEST_VECTOR( _key, _nonce, _assoc, _pt, _ct ) \
test_vector( _key, _nonce, sizeof(_assoc)-1, _assoc, sizeof(_pt)-1, _pt, sizeof(_ct)-1, _ct ) test_vector( _key, _nonce, sizeof( _assoc ) - 1, _assoc, sizeof( _pt ) - 1, _pt, sizeof( _ct ) - 1, _ct )
static void test_all_vectors( void ) { static void test_all_vectors( void )
{
/* Test vectors from http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A */ /* Test vectors from http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A */
const char ietf_key[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; const char ietf_key[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F";
const char ietf_nonce[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"; const char ietf_nonce[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B";
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" /* associated data */ ietf_nonce,
, "" /* plaintext */ "" /* associated data */
,
"" /* plaintext */
/* ciphertext including tag */ /* ciphertext including tag */
, "\x19\x7B\x9C\x3C\x44\x1D\x3C\x83\xEA\xFB\x2B\xEF\x63\x3B\x91\x82" ); ,
"\x19\x7B\x9C\x3C\x44\x1D\x3C\x83\xEA\xFB\x2B\xEF\x63\x3B\x91\x82" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07" "\x00\x01\x02\x03\x04\x05\x06\x07",
, "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x16\xDC\x76\xA4\x6D\x47\xE1\xEA" "\x00\x01\x02\x03\x04\x05\x06\x07",
"\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x16\xDC\x76\xA4\x6D\x47\xE1\xEA"
"\xD5\x37\x20\x9E\x8A\x96\xD1\x4E" ); "\xD5\x37\x20\x9E\x8A\x96\xD1\x4E" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07",
, "\x98\xB9\x15\x52\xC8\xC0\x09\x18\x50\x44\xE3\x0A\x6E\xB2\xFE\x21" ); "",
"\x98\xB9\x15\x52\xC8\xC0\x09\x18\x50\x44\xE3\x0A\x6E\xB2\xFE\x21" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07" "",
, "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x97\x1E\xFF\xCA\xE1\x9A\xD4\x71" "\x00\x01\x02\x03\x04\x05\x06\x07",
"\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x97\x1E\xFF\xCA\xE1\x9A\xD4\x71"
"\x6F\x88\xE8\x7B\x87\x1F\xBE\xED" ); "\x6F\x88\xE8\x7B\x87\x1F\xBE\xED" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x77\x6C\x99\x24\xD6\x72\x3A\x1F\xC4\x52\x45\x32\xAC\x3E\x5B\xEB" ); "\x77\x6C\x99\x24\xD6\x72\x3A\x1F\xC4\x52\x45\x32\xAC\x3E\x5B\xEB" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
, "\x7D\xDB\x8E\x6C\xEA\x68\x14\x86\x62\x12\x50\x96\x19\xB1\x9C\xC6" ); "",
"\x7D\xDB\x8E\x6C\xEA\x68\x14\x86\x62\x12\x50\x96\x19\xB1\x9C\xC6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"\x13\xCC\x8B\x74\x78\x07\x12\x1A\x4C\xBB\x3E\x4B\xD6\xB4\x56\xAF"); "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x13\xCC\x8B\x74\x78\x07\x12\x1A\x4C\xBB\x3E\x4B\xD6\xB4\x56\xAF" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); "\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "" "\x10\x11\x12\x13\x14\x15\x16\x17",
, "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" ); "",
"\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); "\x97\xDC\x7E\xED\xE2\x41\xDF\x68" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); "\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17",
, "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6"); "",
"\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); "\x97\xDC\x7E\xED\xE2\x41\xDF\x68" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\xB2\xA0\x40\xDD\x3B\xD5\x16\x43\x72\xD7\x6D\x7B\xB6\x82\x42\x40" ); "\xB2\xA0\x40\xDD\x3B\xD5\x16\x43\x72\xD7\x6D\x7B\xB6\x82\x42\x40" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
, "\xE1\xE0\x72\x63\x3B\xAD\xE5\x1A\x60\xE8\x59\x51\xD9\xC4\x2A\x1B" ); "",
"\xE1\xE0\x72\x63\x3B\xAD\xE5\x1A\x60\xE8\x59\x51\xD9\xC4\x2A\x1B" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x4A\x3B\xAE\x82\x44\x65\xCF\xDA\xF8\xC4\x1F\xC5\x0C\x7D\xF9\xD9" ); "\x4A\x3B\xAE\x82\x44\x65\xCF\xDA\xF8\xC4\x1F\xC5\x0C\x7D\xF9\xD9" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x20\x21\x22\x23\x24\x25\x26\x27",
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x20\x21\x22\x23\x24\x25\x26\x27",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x68\xC6\x57\x78\xB0\x58\xA6\x35\x65\x9C\x62\x32\x11\xDE\xEA\x0D" "\x68\xC6\x57\x78\xB0\x58\xA6\x35\x65\x9C\x62\x32\x11\xDE\xEA\x0D"
"\xE3\x0D\x2C\x38\x18\x79\xF4\xC8" ); "\xE3\x0D\x2C\x38\x18\x79\xF4\xC8" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x20\x21\x22\x23\x24\x25\x26\x27",
, "" "",
, "\x7A\xEB\x7A\x69\xA1\x68\x7D\xD0\x82\xCA\x27\xB0\xD9\xA3\x70\x96" ); "\x7A\xEB\x7A\x69\xA1\x68\x7D\xD0\x82\xCA\x27\xB0\xD9\xA3\x70\x96" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x20\x21\x22\x23\x24\x25\x26\x27",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x68\xC6\x57\x78\xB0\x58\xA6\x35\x06\x0C\x84\x67\xF4\xAB\xAB\x5E" "\x68\xC6\x57\x78\xB0\x58\xA6\x35\x06\x0C\x84\x67\xF4\xAB\xAB\x5E"
"\x8B\x3C\x20\x67\xA2\xE1\x15\xDC" ); "\x8B\x3C\x20\x67\xA2\xE1\x15\xDC" );
/* Some big texts. These were originally encrypted using this program; /* Some big texts. These were originally encrypted using this program;
they are regression tests. */ they are regression tests. */
TEST_VECTOR( TEST_VECTOR( "\x06\xF8\x9F\x69\xDA\x49\xDA\xD7\x68\x48\xFF\xB3\x60\xB6\x8F\x00",
"\x06\xF8\x9F\x69\xDA\x49\xDA\xD7\x68\x48\xFF\xB3\x60\xB6\x8F\x00" "\xDC\xBF\x85\x18\x23\xD9\x67\x85\x45\x59\x6F\xAD",
, "\xDC\xBF\x85\x18\x23\xD9\x67\x85\x45\x59\x6F\xAD" "",
, "" "\xC4\x8E\x1F\x04\x10\x2F\xA5\x58\x68\x42\x62\xF3\x1B\xE7\x63\xA7"
, "\xC4\x8E\x1F\x04\x10\x2F\xA5\x58\x68\x42\x62\xF3\x1B\xE7\x63\xA7"
"\x77\x89\x64\x16\xE6\xB0\xF7\xFA\xFE\xF0\xB9\x50\x22\xDC\xCE\x78" "\x77\x89\x64\x16\xE6\xB0\xF7\xFA\xFE\xF0\xB9\x50\x22\xDC\xCE\x78"
"\xA5\x01\xA4\x2D\xA2\x0F\x50\xEA\x9A\xAE\x23\x60\x1C\xC9\x11\x84" "\xA5\x01\xA4\x2D\xA2\x0F\x50\xEA\x9A\xAE\x23\x60\x1C\xC9\x11\x84"
"\x5F\xD0\x0A\x88\x99\xCD\xF1\x1B\x7C\xF9\x71\xC2\xD8\xE3\x7B\xB1" "\x5F\xD0\x0A\x88\x99\xCD\xF1\x1B\x7C\xF9\x71\xC2\xD8\xE3\x7B\xB1"
@@ -356,8 +393,8 @@ static void test_all_vectors( void ) {
"\x2C\x2A\x86\x25\x77\x85\x14\x76\xD4\x51\xAB\xC7\x3A\xA7\xE1\xF7" "\x2C\x2A\x86\x25\x77\x85\x14\x76\xD4\x51\xAB\xC7\x3A\xA7\xE1\xF7"
"\x23\xF7\x2B\xA3\xBA\xE4\x0B\xA4\x81\x9A\x83\x98\x69\xC3\x1C\x8A" "\x23\xF7\x2B\xA3\xBA\xE4\x0B\xA4\x81\x9A\x83\x98\x69\xC3\x1C\x8A"
"\xBD\x26\x12\x36\x22\x9D\xCE\x85\x5D\xA3\xA0\xDF\x66\xD0\x59\xF6" "\xBD\x26\x12\x36\x22\x9D\xCE\x85\x5D\xA3\xA0\xDF\x66\xD0\x59\xF6"
"\x47\xF2\xC5\x37\xF1\x62\x0D\x0C\x45\x5B\xE5\xFE\x3C\x8D\x28\x75" "\x47\xF2\xC5\x37\xF1\x62\x0D\x0C\x45\x5B\xE5\xFE\x3C\x8D\x28\x75",
, "\xa1\xd8\xa0\xe0\x75\x5c\xb4\xf4\xab\x59\x6d\x14\xfc\x2e\x75\x54" "\xa1\xd8\xa0\xe0\x75\x5c\xb4\xf4\xab\x59\x6d\x14\xfc\x2e\x75\x54"
"\xa3\x35\x4f\x57\x69\x48\x7a\x46\x17\x5f\xd9\x34\x50\xf9\x35\xe5" "\xa3\x35\x4f\x57\x69\x48\x7a\x46\x17\x5f\xd9\x34\x50\xf9\x35\xe5"
"\x6f\xee\x27\xdb\x28\x0f\x06\x0b\xaf\xd5\x50\x4e\x20\x78\x35\xd6" "\x6f\xee\x27\xdb\x28\x0f\x06\x0b\xaf\xd5\x50\x4e\x20\x78\x35\xd6"
"\x4d\xa0\x18\xe8\x6c\x5b\x07\xbb\xb6\xd0\x3f\x4a\x0e\x14\x32\xaf" "\x4d\xa0\x18\xe8\x6c\x5b\x07\xbb\xb6\xd0\x3f\x4a\x0e\x14\x32\xaf"
@@ -391,11 +428,10 @@ static void test_all_vectors( void ) {
"\xa0\xb8\xe8\xcc\x8a\xe3\xcc\x0c\x92\xe6\xb1\xb0\xe2\xc1\x99\xca" "\xa0\xb8\xe8\xcc\x8a\xe3\xcc\x0c\x92\xe6\xb1\xb0\xe2\xc1\x99\xca"
"\x1b\xa1\xac\x6e\x7a\x8a\xa0\x20\x3d\xeb\x29\x8b\xf4\x55\x41\x62" ); "\x1b\xa1\xac\x6e\x7a\x8a\xa0\x20\x3d\xeb\x29\x8b\xf4\x55\x41\x62" );
TEST_VECTOR( TEST_VECTOR( "\x7A\x54\x0D\x3E\x56\x38\xF7\xC6\xCF\xAB\xF9\x56\xDC\xCA\x14\x23",
"\x7A\x54\x0D\x3E\x56\x38\xF7\xC6\xCF\xAB\xF9\x56\xDC\xCA\x14\x23" "\x9B\x0E\xC1\x15\xD5\xE6\xC9\xAB\xE6\x88\x2A\x18",
, "\x9B\x0E\xC1\x15\xD5\xE6\xC9\xAB\xE6\x88\x2A\x18" "",
, "" "\x52\xDB\xA7\x44\x2B\x1C\x9C\x24\x4D\xF3\xA1\xE4\x53\x7B\x9B\xB2"
, "\x52\xDB\xA7\x44\x2B\x1C\x9C\x24\x4D\xF3\xA1\xE4\x53\x7B\x9B\xB2"
"\x25\xC5\xA3\x81\x42\x23\xA9\xB4\x12\xF8\xFC\xE4\xF6\x8E\x20\xD4" "\x25\xC5\xA3\x81\x42\x23\xA9\xB4\x12\xF8\xFC\xE4\xF6\x8E\x20\xD4"
"\x59\x7B\x39\x2D\x5D\x7C\x6E\xB7\x51\x02\x90\x7A\x8E\xAA\x30\xD0" "\x59\x7B\x39\x2D\x5D\x7C\x6E\xB7\x51\x02\x90\x7A\x8E\xAA\x30\xD0"
"\xEB\xDF\x70\x09\x5A\xEC\xFB\xD4\xDB\x0B\xE9\x1B\x79\xAF\x40\xA3" "\xEB\xDF\x70\x09\x5A\xEC\xFB\xD4\xDB\x0B\xE9\x1B\x79\xAF\x40\xA3"
@@ -426,8 +462,8 @@ static void test_all_vectors( void ) {
"\x9B\x3E\x3D\x6B\x56\x3B\x2B\xDC\x8A\x46\xF6\x7C\x36\xF9\x27\x29" "\x9B\x3E\x3D\x6B\x56\x3B\x2B\xDC\x8A\x46\xF6\x7C\x36\xF9\x27\x29"
"\x37\x38\x7C\x9D\xA0\x6E\x5D\x4C\xE5\xB2\x6F\x0C\xDC\xEF\xFE\x35" "\x37\x38\x7C\x9D\xA0\x6E\x5D\x4C\xE5\xB2\x6F\x0C\xDC\xEF\xFE\x35"
"\xFE\x3D\x56\x40\x7F\xBD\x4D\xDD\x40\x79\xDD\xA7\x0A\x7B\xA2\xCE" "\xFE\x3D\x56\x40\x7F\xBD\x4D\xDD\x40\x79\xDD\xA7\x0A\x7B\xA2\xCE"
"\x22\x38\x94\xEA\x90\xF5\x95\xB6\xE6\x6F\x14\xFB\xA2" "\x22\x38\x94\xEA\x90\xF5\x95\xB6\xE6\x6F\x14\xFB\xA2",
, "\xec\xa9\xcc\x30\x66\x6c\x04\x16\x21\x8d\xc8\x15\x47\xa2\x18\xcf" "\xec\xa9\xcc\x30\x66\x6c\x04\x16\x21\x8d\xc8\x15\x47\xa2\x18\xcf"
"\x19\x90\x4f\x82\x27\x25\xa2\x1e\xfa\x1c\xe4\x58\x78\x43\x52\x4c" "\x19\x90\x4f\x82\x27\x25\xa2\x1e\xfa\x1c\xe4\x58\x78\x43\x52\x4c"
"\xac\x24\xde\xcb\xad\x80\x05\x7a\xeb\x2d\xc0\x33\x05\x31\x25\x44" "\xac\x24\xde\xcb\xad\x80\x05\x7a\xeb\x2d\xc0\x33\x05\x31\x25\x44"
"\xd7\x11\xa1\xf2\xcb\x09\x6f\xf0\x14\x3c\x3f\xf2\xc7\x79\xfb\x3f" "\xd7\x11\xa1\xf2\xcb\x09\x6f\xf0\x14\x3c\x3f\xf2\xc7\x79\xfb\x3f"
@@ -465,13 +501,14 @@ static void test_all_vectors( void ) {
/* http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A /* http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A
also specifies an iterative test algorithm, which we implement here. */ also specifies an iterative test algorithm, which we implement here. */
static void test_iterative( void ) { static void test_iterative( void )
{
/* Key is always all zeros */ /* Key is always all zeros */
AlignedBuffer key( KEY_LEN ); AlignedBuffer key( KEY_LEN );
memset( key.data(), 0, KEY_LEN ); memset( key.data(), 0, KEY_LEN );
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer nonce( NONCE_LEN ); AlignedBuffer nonce( NONCE_LEN );
memset( nonce.data(), 0, NONCE_LEN ); memset( nonce.data(), 0, NONCE_LEN );
@@ -479,43 +516,32 @@ static void test_iterative( void ) {
/* The loop below fills this buffer in order. /* The loop below fills this buffer in order.
Each iteration adds 2*i + 3*TAG_LEN bytes. */ Each iteration adds 2*i + 3*TAG_LEN bytes. */
AlignedBuffer accumulator( 22400 ); AlignedBuffer accumulator( 22400 );
uint8_t *acc = (uint8_t *) accumulator.data(); uint8_t* acc = (uint8_t*)accumulator.data();
for ( size_t i=0; i<128; i++ ) { for ( size_t i = 0; i < 128; i++ ) {
/* i bytes of zeros */ /* i bytes of zeros */
AlignedBuffer s( i ); AlignedBuffer s( i );
memset( s.data(), 0, s.len() ); memset( s.data(), 0, s.len() );
/* Nonce is 11 bytes of zeros followed by 1 byte i */ /* Nonce is 11 bytes of zeros followed by 1 byte i */
( (uint8_t *) nonce.data() )[ 11 ] = i; ( (uint8_t*)nonce.data() )[11] = i;
/* We can't write directly to acc because it might not be aligned. */ /* We can't write directly to acc because it might not be aligned. */
AlignedBuffer out( s.len() + TAG_LEN ); AlignedBuffer out( s.len() + TAG_LEN );
/* OCB-ENCRYPT(K,N,S,S) */ /* OCB-ENCRYPT(K,N,S,S) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert(
s.data(), s.len(), 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) );
s.data(), s.len(),
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), s.len() + TAG_LEN ); memcpy( acc, out.data(), s.len() + TAG_LEN );
acc += s.len() + TAG_LEN; acc += s.len() + TAG_LEN;
/* OCB-ENCRYPT(K,N,<empty string>,S) */ /* OCB-ENCRYPT(K,N,<empty string>,S) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), NULL, 0, out.data(), NULL, AE_FINALIZE ) );
s.data(), s.len(),
NULL, 0,
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), s.len() + TAG_LEN ); memcpy( acc, out.data(), s.len() + TAG_LEN );
acc += s.len() + TAG_LEN; acc += s.len() + TAG_LEN;
/* OCB-ENCRYPT(K,N,S,<empty string>) */ /* OCB-ENCRYPT(K,N,S,<empty string>) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), NULL, 0, s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) );
NULL, 0,
s.data(), s.len(),
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), TAG_LEN ); memcpy( acc, out.data(), TAG_LEN );
acc += TAG_LEN; acc += TAG_LEN;
} }
@@ -523,11 +549,9 @@ static void test_iterative( void ) {
/* OCB-ENCRYPT(K,N,C,<empty string>) */ /* OCB-ENCRYPT(K,N,C,<empty string>) */
AlignedBuffer out( TAG_LEN ); AlignedBuffer out( TAG_LEN );
memset( nonce.data(), 0, NONCE_LEN ); memset( nonce.data(), 0, NONCE_LEN );
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert(
NULL, 0, 0 <= ae_encrypt(
accumulator.data(), accumulator.len(), ctx, nonce.data(), NULL, 0, accumulator.data(), accumulator.len(), out.data(), NULL, AE_FINALIZE ) );
out.data(), NULL,
AE_FINALIZE ) );
/* Check this final tag against the known value */ /* Check this final tag against the known value */
AlignedBuffer correct( TAG_LEN, "\xB2\xB4\x1C\xBF\x9B\x05\x03\x7D\xA7\xF1\x6C\x24\xA3\x5C\x1C\x94" ); AlignedBuffer correct( TAG_LEN, "\xB2\xB4\x1C\xBF\x9B\x05\x03\x7D\xA7\xF1\x6C\x24\xA3\x5C\x1C\x94" );
@@ -539,16 +563,16 @@ static void test_iterative( void ) {
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
try { try {
test_all_vectors(); test_all_vectors();
test_iterative(); test_iterative();
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
return 1; return 1;
} }
+8 -5
View File
@@ -34,22 +34,25 @@
#include "test_utils.h" #include "test_utils.h"
void hexdump( const void *buf, size_t len, const char *name ) { void hexdump( const void* buf, size_t len, const char* name )
const unsigned char *data = (const unsigned char *) buf; {
const unsigned char* data = (const unsigned char*)buf;
printf( DUMP_NAME_FMT, name ); printf( DUMP_NAME_FMT, name );
for ( size_t i = 0; i < len; i++ ) { for ( size_t i = 0; i < len; i++ ) {
// Although data[i] is an unsigned char, it will be promoted to a signed int // Although data[i] is an unsigned char, it will be promoted to a signed int
// when passed as an argument. Explicitly cast it back to an unsigned type // when passed as an argument. Explicitly cast it back to an unsigned type
// so it can be printed in hex. // so it can be printed in hex.
printf( "%02x", static_cast<unsigned>( data[ i ] ) ); printf( "%02x", static_cast<unsigned>( data[i] ) );
} }
printf( "\n" ); printf( "\n" );
} }
void hexdump( const Crypto::AlignedBuffer &buf, const char *name ) { void hexdump( const Crypto::AlignedBuffer& buf, const char* name )
{
hexdump( buf.data(), buf.len(), name ); hexdump( buf.data(), buf.len(), name );
} }
void hexdump( const std::string &buf, const char *name ) { void hexdump( const std::string& buf, const char* name )
{
hexdump( buf.data(), buf.size(), name ); hexdump( buf.data(), buf.size(), name );
} }
+3 -3
View File
@@ -39,8 +39,8 @@
#define DUMP_NAME_FMT "%-10s " #define DUMP_NAME_FMT "%-10s "
void hexdump( const void *buf, size_t len, const char *name ); void hexdump( const void* buf, size_t len, const char* name );
void hexdump( const Crypto::AlignedBuffer &buf, const char *name ); void hexdump( const Crypto::AlignedBuffer& buf, const char* name );
void hexdump( const std::string &buf, const char *name ); void hexdump( const std::string& buf, const char* name );
#endif #endif
+10 -8
View File
@@ -38,17 +38,19 @@
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
static void dos_detected( const char *expression, const char *file, int line, const char *function ) static void dos_detected( const char* expression, const char* file, int line, const char* function )
{ {
char buffer[ 2048 ]; char buffer[2048];
snprintf( buffer, 2048, "Illegal counterparty input (possible denial of service) in function %s at %s:%d, failed test: %s\n", snprintf( buffer,
function, file, line, expression ); 2048,
"Illegal counterparty input (possible denial of service) in function %s at %s:%d, failed test: %s\n",
function,
file,
line,
expression );
throw Crypto::CryptoException( buffer ); throw Crypto::CryptoException( buffer );
} }
#define dos_assert(expr) \ #define dos_assert( expr ) ( ( expr ) ? (void)0 : dos_detected( #expr, __FILE__, __LINE__, __func__ ) )
((expr) \
? (void)0 \
: dos_detected (#expr, __FILE__, __LINE__, __func__ ))
#endif #endif
+8 -7
View File
@@ -36,16 +36,17 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
static void fatal_error( const char *expression, const char *file, int line, const char *function ) static void fatal_error( const char* expression, const char* file, int line, const char* function )
{ {
fprintf( stderr, "Fatal assertion failure in function %s at %s:%d\nFailed test: %s\n", fprintf( stderr,
function, file, line, expression ); "Fatal assertion failure in function %s at %s:%d\nFailed test: %s\n",
function,
file,
line,
expression );
abort(); abort();
} }
#define fatal_assert(expr) \ #define fatal_assert( expr ) ( ( expr ) ? (void)0 : fatal_error( #expr, __FILE__, __LINE__, __func__ ) )
((expr) \
? (void)0 \
: fatal_error (#expr, __FILE__, __LINE__, __func__ ))
#endif #endif
+13 -13
View File
@@ -45,7 +45,6 @@
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
const std::string LocaleVar::str( void ) const const std::string LocaleVar::str( void ) const
{ {
if ( name.empty() ) { if ( name.empty() ) {
@@ -57,22 +56,22 @@ const std::string LocaleVar::str( void ) const
const LocaleVar get_ctype( void ) const LocaleVar get_ctype( void )
{ {
/* Reimplement the search logic, just for diagnostics */ /* Reimplement the search logic, just for diagnostics */
if ( const char *all = getenv( "LC_ALL" ) ) { if ( const char* all = getenv( "LC_ALL" ) ) {
return LocaleVar( "LC_ALL", all ); return LocaleVar( "LC_ALL", all );
} else if ( const char *ctype = getenv( "LC_CTYPE" ) ) { } else if ( const char* ctype = getenv( "LC_CTYPE" ) ) {
return LocaleVar( "LC_CTYPE", ctype ); return LocaleVar( "LC_CTYPE", ctype );
} else if ( const char *lang = getenv( "LANG" ) ) { } else if ( const char* lang = getenv( "LANG" ) ) {
return LocaleVar( "LANG", lang ); return LocaleVar( "LANG", lang );
} }
return LocaleVar( "", "" ); return LocaleVar( "", "" );
} }
const char *locale_charset( void ) const char* locale_charset( void )
{ {
static const char ASCII_name[] = "US-ASCII"; static const char ASCII_name[] = "US-ASCII";
/* Produce more pleasant name of US-ASCII */ /* Produce more pleasant name of US-ASCII */
const char *ret = nl_langinfo( CODESET ); const char* ret = nl_langinfo( CODESET );
if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) { if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) {
ret = ASCII_name; ret = ASCII_name;
@@ -81,16 +80,17 @@ const char *locale_charset( void )
return ret; return ret;
} }
bool is_utf8_locale( void ) { bool is_utf8_locale( void )
{
/* Verify locale calls for UTF-8 */ /* Verify locale calls for UTF-8 */
if ( strcmp( locale_charset(), "UTF-8" ) != 0 && if ( strcmp( locale_charset(), "UTF-8" ) != 0 && strcmp( locale_charset(), "utf-8" ) != 0 ) {
strcmp( locale_charset(), "utf-8" ) != 0 ) {
return false; return false;
} }
return true; return true;
} }
void set_native_locale( void ) { void set_native_locale( void )
{
/* Adopt native locale */ /* Adopt native locale */
if ( NULL == setlocale( LC_ALL, "" ) ) { if ( NULL == setlocale( LC_ALL, "" ) ) {
int saved_errno = errno; int saved_errno = errno;
@@ -98,8 +98,7 @@ void set_native_locale( void ) {
LocaleVar ctype( get_ctype() ); LocaleVar ctype( get_ctype() );
fprintf( stderr, "The locale requested by %s isn't available here.\n", ctype.str().c_str() ); fprintf( stderr, "The locale requested by %s isn't available here.\n", ctype.str().c_str() );
if ( !ctype.name.empty() ) { if ( !ctype.name.empty() ) {
fprintf( stderr, "Running `locale-gen %s' may be necessary.\n\n", fprintf( stderr, "Running `locale-gen %s' may be necessary.\n\n", ctype.value.c_str() );
ctype.value.c_str() );
} }
} else { } else {
errno = saved_errno; errno = saved_errno;
@@ -108,7 +107,8 @@ void set_native_locale( void ) {
} }
} }
void clear_locale_variables( void ) { void clear_locale_variables( void )
{
unsetenv( "LANG" ); unsetenv( "LANG" );
unsetenv( "LANGUAGE" ); unsetenv( "LANGUAGE" );
unsetenv( "LC_CTYPE" ); unsetenv( "LC_CTYPE" );
+5 -6
View File
@@ -35,17 +35,16 @@
#include <string> #include <string>
class LocaleVar { class LocaleVar
public: {
public:
const std::string name, value; const std::string name, value;
LocaleVar( const char *s_name, const char *s_value ) LocaleVar( const char* s_name, const char* s_value ) : name( s_name ), value( s_value ) {}
: name( s_name ), value( s_value )
{}
const std::string str( void ) const; const std::string str( void ) const;
}; };
const LocaleVar get_ctype( void ); const LocaleVar get_ctype( void );
const char *locale_charset( void ); const char* locale_charset( void );
bool is_utf8_locale( void ); bool is_utf8_locale( void );
void set_native_locale( void ); void set_native_locale( void );
void clear_locale_variables( void ); void clear_locale_variables( void );
+14 -18
View File
@@ -32,7 +32,7 @@
#include "src/include/config.h" #include "src/include/config.h"
#if !defined(HAVE_FORKPTY) || !defined(HAVE_CFMAKERAW) #if !defined( HAVE_FORKPTY ) || !defined( HAVE_CFMAKERAW )
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -46,13 +46,11 @@
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
pid_t my_forkpty( int *amaster, char *name, pid_t my_forkpty( int* amaster, char* name, const struct termios* termp, const struct winsize* winp )
const struct termios *termp,
const struct winsize *winp )
{ {
/* For Solaris and AIX */ /* For Solaris and AIX */
int master, slave; int master, slave;
char *slave_name; char* slave_name;
pid_t pid; pid_t pid;
#ifdef _AIX #ifdef _AIX
@@ -73,7 +71,7 @@ pid_t my_forkpty( int *amaster, char *name,
return -1; return -1;
} }
if ( unlockpt(master) < 0 ) { if ( unlockpt( master ) < 0 ) {
perror( "unlockpt" ); perror( "unlockpt" );
close( master ); close( master );
return -1; return -1;
@@ -94,8 +92,7 @@ pid_t my_forkpty( int *amaster, char *name,
} }
#ifndef _AIX #ifndef _AIX
if ( ioctl(slave, I_PUSH, "ptem") < 0 || if ( ioctl( slave, I_PUSH, "ptem" ) < 0 || ioctl( slave, I_PUSH, "ldterm" ) < 0 ) {
ioctl(slave, I_PUSH, "ldterm") < 0 ) {
perror( "ioctl(I_PUSH)" ); perror( "ioctl(I_PUSH)" );
close( slave ); close( slave );
close( master ); close( master );
@@ -106,7 +103,7 @@ pid_t my_forkpty( int *amaster, char *name,
if ( amaster != NULL ) if ( amaster != NULL )
*amaster = master; *amaster = master;
if ( name != NULL) if ( name != NULL )
strcpy( name, slave_name ); strcpy( name, slave_name );
if ( termp != NULL ) { if ( termp != NULL ) {
@@ -122,7 +119,7 @@ pid_t my_forkpty( int *amaster, char *name,
w.ws_col = 80; w.ws_col = 80;
w.ws_xpixel = 0; w.ws_xpixel = 0;
w.ws_ypixel = 0; w.ws_ypixel = 0;
if ( ioctl( slave, TIOCSWINSZ, &w) < 0 ) { if ( ioctl( slave, TIOCSWINSZ, &w ) < 0 ) {
perror( "ioctl TIOCSWINSZ" ); perror( "ioctl TIOCSWINSZ" );
exit( 1 ); exit( 1 );
} }
@@ -149,12 +146,12 @@ pid_t my_forkpty( int *amaster, char *name,
#else #else
{ {
int dummy_fd; int dummy_fd;
dummy_fd = open (slave_name, O_RDWR); dummy_fd = open( slave_name, O_RDWR );
if (dummy_fd < 0) { if ( dummy_fd < 0 ) {
perror( "open(slave_name)" ); perror( "open(slave_name)" );
return -1; return -1;
} }
close (dummy_fd); close( dummy_fd );
} }
#endif /* TIOCSCTTY */ #endif /* TIOCSCTTY */
close( master ); close( master );
@@ -170,13 +167,12 @@ pid_t my_forkpty( int *amaster, char *name,
#endif #endif
#ifndef HAVE_CFMAKERAW #ifndef HAVE_CFMAKERAW
void my_cfmakeraw( struct termios *termios_p ) void my_cfmakeraw( struct termios* termios_p )
{ {
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP termios_p->c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON );
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST; termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); termios_p->c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
termios_p->c_cflag &= ~(CSIZE | PARENB); termios_p->c_cflag &= ~( CSIZE | PARENB );
termios_p->c_cflag |= CS8; termios_p->c_cflag |= CS8;
termios_p->c_cc[VMIN] = 1; // read() is satisfied after 1 char termios_p->c_cc[VMIN] = 1; // read() is satisfied after 1 char
+4 -6
View File
@@ -36,16 +36,14 @@
#include "src/include/config.h" #include "src/include/config.h"
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
# define forkpty my_forkpty #define forkpty my_forkpty
#endif #endif
#ifndef HAVE_CFMAKERAW #ifndef HAVE_CFMAKERAW
# define cfmakeraw my_cfmakeraw #define cfmakeraw my_cfmakeraw
#endif #endif
pid_t my_forkpty( int *amaster, char *name, pid_t my_forkpty( int* amaster, char* name, const struct termios* termp, const struct winsize* winp );
const struct termios *termp,
const struct winsize *winp );
void my_cfmakeraw( struct termios *termios_p ); void my_cfmakeraw( struct termios* termios_p );
#endif #endif
+2 -2
View File
@@ -43,6 +43,6 @@ void Select::handle_signal( int signum )
fatal_assert( signum >= 0 ); fatal_assert( signum >= 0 );
fatal_assert( signum <= MAX_SIGNAL_NUMBER ); fatal_assert( signum <= MAX_SIGNAL_NUMBER );
Select &sel = get_instance(); Select& sel = get_instance();
sel.got_signal[ signum ] = 1; sel.got_signal[signum] = 1;
} }
+23 -24
View File
@@ -48,9 +48,11 @@
Any signals blocked by calling sigprocmask() outside this code will still be Any signals blocked by calling sigprocmask() outside this code will still be
received during Select::select(). So don't do that. */ received during Select::select(). So don't do that. */
class Select { class Select
{
public: public:
static Select &get_instance( void ) { static Select& get_instance( void )
{
/* COFU may or may not be thread-safe, depending on compiler */ /* COFU may or may not be thread-safe, depending on compiler */
static Select instance; static Select instance;
return instance; return instance;
@@ -61,10 +63,8 @@ private:
: max_fd( -1 ) : max_fd( -1 )
/* These initializations are not used; they are just /* These initializations are not used; they are just
here to appease -Weffc++. */ here to appease -Weffc++. */
, all_fds( dummy_fd_set ) ,
, read_fds( dummy_fd_set ) all_fds( dummy_fd_set ), read_fds( dummy_fd_set ), empty_sigset( dummy_sigset ), consecutive_polls( 0 )
, empty_sigset( dummy_sigset )
, consecutive_polls( 0 )
{ {
FD_ZERO( &all_fds ); FD_ZERO( &all_fds );
FD_ZERO( &read_fds ); FD_ZERO( &read_fds );
@@ -75,16 +75,15 @@ private:
void clear_got_signal( void ) void clear_got_signal( void )
{ {
for ( volatile sig_atomic_t *p = got_signal; for ( volatile sig_atomic_t* p = got_signal; p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
p++ ) { p++ ) {
*p = 0; *p = 0;
} }
} }
/* not implemented */ /* not implemented */
Select( const Select & ); Select( const Select& );
Select &operator=( const Select & ); Select& operator=( const Select& );
public: public:
void add_fd( int fd ) void add_fd( int fd )
@@ -95,10 +94,7 @@ public:
FD_SET( fd, &all_fds ); FD_SET( fd, &all_fds );
} }
void clear_fds( void ) void clear_fds( void ) { FD_ZERO( &all_fds ); }
{
FD_ZERO( &all_fds );
}
static void add_signal( int signum ) static void add_signal( int signum )
{ {
@@ -144,23 +140,23 @@ public:
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
struct timespec ts; struct timespec ts;
struct timespec *tsp = NULL; struct timespec* tsp = NULL;
if ( timeout >= 0 ) { if ( timeout >= 0 ) {
ts.tv_sec = timeout / 1000; ts.tv_sec = timeout / 1000;
ts.tv_nsec = 1000000 * (long( timeout ) % 1000); ts.tv_nsec = 1000000 * ( long( timeout ) % 1000 );
tsp = &ts; tsp = &ts;
} }
int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset ); int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset );
#else #else
struct timeval tv; struct timeval tv;
struct timeval *tvp = NULL; struct timeval* tvp = NULL;
sigset_t old_sigset; sigset_t old_sigset;
if ( timeout >= 0 ) { if ( timeout >= 0 ) {
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = 1000 * (long( timeout ) % 1000); tv.tv_usec = 1000 * ( long( timeout ) % 1000 );
tvp = &tv; tvp = &tv;
} }
@@ -205,8 +201,8 @@ public:
fatal_assert( signum >= 0 ); fatal_assert( signum >= 0 );
fatal_assert( signum <= MAX_SIGNAL_NUMBER ); fatal_assert( signum <= MAX_SIGNAL_NUMBER );
/* XXX This requires a guard against concurrent signals. */ /* XXX This requires a guard against concurrent signals. */
bool rv = got_signal[ signum ]; bool rv = got_signal[signum];
got_signal[ signum ] = 0; got_signal[signum] = 0;
return rv; return rv;
} }
@@ -214,13 +210,16 @@ public:
bool any_signal( void ) const bool any_signal( void ) const
{ {
bool rv = false; bool rv = false;
for (int i = 0; i < MAX_SIGNAL_NUMBER; i++) { for ( int i = 0; i < MAX_SIGNAL_NUMBER; i++ ) {
rv |= got_signal[ i ]; rv |= got_signal[i];
} }
return rv; return rv;
} }
static void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; } static void set_verbose( unsigned int s_verbose )
{
verbose = s_verbose;
}
private: private:
static const int MAX_SIGNAL_NUMBER = 64; static const int MAX_SIGNAL_NUMBER = 64;
@@ -234,7 +233,7 @@ private:
/* We assume writes to got_signal are atomic, though we also try to mask out /* We assume writes to got_signal are atomic, though we also try to mask out
concurrent signal handlers. */ concurrent signal handlers. */
volatile sig_atomic_t got_signal[ MAX_SIGNAL_NUMBER + 1 ]; volatile sig_atomic_t got_signal[MAX_SIGNAL_NUMBER + 1];
fd_set all_fds, read_fds; fd_set all_fds, read_fds;
+3 -4
View File
@@ -37,13 +37,12 @@
#include "src/util/swrite.h" #include "src/util/swrite.h"
int swrite( int fd, const char *str, ssize_t len ) int swrite( int fd, const char* str, ssize_t len )
{ {
ssize_t total_bytes_written = 0; ssize_t total_bytes_written = 0;
ssize_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t) strlen( str ); ssize_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t)strlen( str );
while ( total_bytes_written < bytes_to_write ) { while ( total_bytes_written < bytes_to_write ) {
ssize_t bytes_written = write( fd, str + total_bytes_written, ssize_t bytes_written = write( fd, str + total_bytes_written, bytes_to_write - total_bytes_written );
bytes_to_write - total_bytes_written );
if ( bytes_written <= 0 ) { if ( bytes_written <= 0 ) {
perror( "write" ); perror( "write" );
return -1; return -1;
+1 -1
View File
@@ -33,6 +33,6 @@
#ifndef SWRITE_HPP #ifndef SWRITE_HPP
#define SWRITE_HPP #define SWRITE_HPP
int swrite( int fd, const char *str, ssize_t len = -1 ); int swrite( int fd, const char* str, ssize_t len = -1 );
#endif #endif
+8 -8
View File
@@ -44,15 +44,15 @@
#include <mach/mach_time.h> #include <mach/mach_time.h>
#endif #endif
#if HAVE_GETTIMEOFDAY #if HAVE_GETTIMEOFDAY
#include <sys/time.h>
#include <cstdio> #include <cstdio>
#include <sys/time.h>
#endif #endif
// On Apple systems CLOCK_MONOTONIC is unfortunately able to go // On Apple systems CLOCK_MONOTONIC is unfortunately able to go
// backwards in time. This breaks mosh when system is returning from // backwards in time. This breaks mosh when system is returning from
// suspend as described in ticket #1014. To avoid this issue prefer // suspend as described in ticket #1014. To avoid this issue prefer
// CLOCK_MONOTONIC_RAW on Apple systems when available. // CLOCK_MONOTONIC_RAW on Apple systems when available.
#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW) #if defined( __APPLE__ ) && defined( CLOCK_MONOTONIC_RAW )
#define CLOCKTYPE CLOCK_MONOTONIC_RAW #define CLOCKTYPE CLOCK_MONOTONIC_RAW
#else #else
#define CLOCKTYPE CLOCK_MONOTONIC #define CLOCKTYPE CLOCK_MONOTONIC
@@ -79,7 +79,7 @@ void freeze_timestamp( void )
struct timespec tp; struct timespec tp;
if ( if (
#if defined(__APPLE__) && defined(__MACH__) #if defined( __APPLE__ ) && defined( __MACH__ )
// Check for presence, for OS X SDK >= 10.12 and runtime < 10.12 // Check for presence, for OS X SDK >= 10.12 and runtime < 10.12
&clock_gettime != NULL && &clock_gettime != NULL &&
#endif #endif
@@ -97,8 +97,8 @@ void freeze_timestamp( void )
static mach_timebase_info_data_t s_timebase_info; static mach_timebase_info_data_t s_timebase_info;
static double absolute_to_millis = 0.0; static double absolute_to_millis = 0.0;
if (absolute_to_millis == 0.0) { if ( absolute_to_millis == 0.0 ) {
if (ERR_SUCCESS == mach_timebase_info(&s_timebase_info)) { if ( ERR_SUCCESS == mach_timebase_info( &s_timebase_info ) ) {
absolute_to_millis = 1e-6 * s_timebase_info.numer / s_timebase_info.denom; absolute_to_millis = 1e-6 * s_timebase_info.numer / s_timebase_info.denom;
} else } else
absolute_to_millis = -1.0; absolute_to_millis = -1.0;
@@ -106,7 +106,7 @@ void freeze_timestamp( void )
// NB: mach_absolute_time() returns "absolute time units" // NB: mach_absolute_time() returns "absolute time units"
// We need to apply a conversion to get milliseconds. // We need to apply a conversion to get milliseconds.
if (absolute_to_millis > 0.0) { if ( absolute_to_millis > 0.0 ) {
millis_cache = mach_absolute_time() * absolute_to_millis; millis_cache = mach_absolute_time() * absolute_to_millis;
return; return;
} }
@@ -115,7 +115,7 @@ void freeze_timestamp( void )
// Not monotonic. // Not monotonic.
// NOTE: If time steps backwards, timeouts may be confused. // NOTE: If time steps backwards, timeouts may be confused.
struct timeval tv; struct timeval tv;
if ( gettimeofday(&tv, NULL) ) { if ( gettimeofday( &tv, NULL ) ) {
perror( "gettimeofday" ); perror( "gettimeofday" );
} else { } else {
uint64_t millis = tv.tv_usec / 1000; uint64_t millis = tv.tv_usec / 1000;
@@ -125,6 +125,6 @@ void freeze_timestamp( void )
return; return;
} }
#else #else
# error "gettimeofday() unavailable-- required as timer of last resort" #error "gettimeofday() unavailable-- required as timer of last resort"
#endif #endif
} }