mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
463 lines
11 KiB
C++
463 lines
11 KiB
C++
/*
|
|
Copyright (C) 2004 Artem Khodush
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
3. The name of the author may not be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "exec-stream.h"
|
|
|
|
#include <list>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <exception>
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define NOMINMAX
|
|
#include <windows.h>
|
|
|
|
#define HELPERS_H "win/exec-stream-helpers.h"
|
|
#define HELPERS_CPP "win/exec-stream-helpers.cpp"
|
|
#define IMPL_CPP "win/exec-stream-impl.cpp"
|
|
|
|
#else
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <pthread.h>
|
|
|
|
#define HELPERS_H "posix/exec-stream-helpers.h"
|
|
#define HELPERS_CPP "posix/exec-stream-helpers.cpp"
|
|
#define IMPL_CPP "posix/exec-stream-impl.cpp"
|
|
|
|
#endif
|
|
|
|
// helper classes
|
|
namespace exec_stream_internal {
|
|
|
|
class buffer_list_t {
|
|
public:
|
|
struct buffer_t {
|
|
std::size_t size;
|
|
char * data;
|
|
};
|
|
|
|
buffer_list_t();
|
|
~buffer_list_t();
|
|
|
|
void get( char * dst, std::size_t & size );
|
|
void get_translate_crlf( char * dst, std::size_t & size );
|
|
void put( char * const src, std::size_t size );
|
|
void put_translate_crlf( char * const src, std::size_t size );
|
|
buffer_t detach();
|
|
|
|
bool empty();
|
|
bool full( std::size_t limit ); // limit==0 -> no limit
|
|
|
|
void clear();
|
|
|
|
private:
|
|
typedef std::list< buffer_t > buffers_t;
|
|
buffers_t m_buffers;
|
|
std::size_t m_read_offset; // offset into the first buffer
|
|
std::size_t m_total_size;
|
|
};
|
|
|
|
buffer_list_t::buffer_list_t()
|
|
{
|
|
m_total_size=0;
|
|
m_read_offset=0;
|
|
}
|
|
|
|
buffer_list_t::~buffer_list_t()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void buffer_list_t::get( char * dst, std::size_t & size )
|
|
{
|
|
std::size_t written_size=0;
|
|
while( size>0 && m_total_size>0 ) {
|
|
std::size_t portion_size=std::min( size, m_buffers.front().size-m_read_offset );
|
|
std::char_traits< char >::copy( dst, m_buffers.front().data+m_read_offset, portion_size );
|
|
dst+=portion_size;
|
|
size-=portion_size;
|
|
m_total_size-=portion_size;
|
|
m_read_offset+=portion_size;
|
|
written_size+=portion_size;
|
|
if( m_read_offset==m_buffers.front().size ) {
|
|
delete[] m_buffers.front().data;
|
|
m_buffers.pop_front();
|
|
m_read_offset=0;
|
|
}
|
|
}
|
|
size=written_size;
|
|
}
|
|
|
|
void buffer_list_t::get_translate_crlf( char * dst, std::size_t & size )
|
|
{
|
|
std::size_t written_size=0;
|
|
while( written_size!=size && m_total_size>0 ) {
|
|
while( written_size!=size && m_read_offset!=m_buffers.front().size ) {
|
|
char c=m_buffers.front().data[m_read_offset];
|
|
if( c!='\r' ) { // MISFEATURE: single \r in the buffer will cause end of file
|
|
*dst++=c;
|
|
++written_size;
|
|
}
|
|
--m_total_size;
|
|
++m_read_offset;
|
|
}
|
|
if( m_read_offset==m_buffers.front().size ) {
|
|
delete[] m_buffers.front().data;
|
|
m_buffers.pop_front();
|
|
m_read_offset=0;
|
|
}
|
|
}
|
|
size=written_size;
|
|
}
|
|
|
|
void buffer_list_t::put( char * const src, std::size_t size )
|
|
{
|
|
buffer_t buffer;
|
|
buffer.data=new char[size];
|
|
buffer.size=size;
|
|
std::char_traits< char >::copy( buffer.data, src, size );
|
|
m_buffers.push_back( buffer );
|
|
m_total_size+=buffer.size;
|
|
}
|
|
|
|
void buffer_list_t::put_translate_crlf( char * const src, std::size_t size )
|
|
{
|
|
char const * p=src;
|
|
std::size_t lf_count=0;
|
|
while( p!=src+size ) {
|
|
if( *p=='\n' ) {
|
|
++lf_count;
|
|
}
|
|
++p;
|
|
}
|
|
buffer_t buffer;
|
|
buffer.data=new char[size+lf_count];
|
|
buffer.size=size+lf_count;
|
|
p=src;
|
|
char * dst=buffer.data;
|
|
while( p!=src+size ) {
|
|
if( *p=='\n' ) {
|
|
*dst++='\r';
|
|
}
|
|
*dst++=*p;
|
|
++p;
|
|
}
|
|
m_buffers.push_back( buffer );
|
|
m_total_size+=buffer.size;
|
|
}
|
|
|
|
buffer_list_t::buffer_t buffer_list_t::detach()
|
|
{
|
|
buffer_t buffer=m_buffers.front();
|
|
m_buffers.pop_front();
|
|
m_total_size-=buffer.size;
|
|
return buffer;
|
|
}
|
|
|
|
bool buffer_list_t::empty()
|
|
{
|
|
return m_total_size==0;
|
|
}
|
|
|
|
bool buffer_list_t::full( std::size_t limit )
|
|
{
|
|
return limit!=0 && m_total_size>=limit;
|
|
}
|
|
|
|
void buffer_list_t::clear()
|
|
{
|
|
for( buffers_t::iterator i=m_buffers.begin(); i!=m_buffers.end(); ++i ) {
|
|
delete[] i->data;
|
|
}
|
|
m_buffers.clear();
|
|
m_read_offset=0;
|
|
m_total_size=0;
|
|
}
|
|
|
|
}
|
|
|
|
// platform-dependent helpers
|
|
|
|
namespace exec_stream_internal {
|
|
|
|
#include HELPERS_H
|
|
#include HELPERS_CPP
|
|
|
|
}
|
|
|
|
// stream buffer class
|
|
namespace exec_stream_internal {
|
|
|
|
class exec_stream_buffer_t : public std::streambuf {
|
|
public:
|
|
exec_stream_buffer_t( exec_stream_t::stream_kind_t kind, thread_buffer_t & thread_buffer );
|
|
virtual ~exec_stream_buffer_t();
|
|
|
|
void clear();
|
|
|
|
protected:
|
|
virtual int_type underflow();
|
|
virtual int_type overflow( int_type c );
|
|
virtual int sync();
|
|
|
|
private:
|
|
bool send_buffer();
|
|
bool send_char( char c );
|
|
|
|
exec_stream_t::stream_kind_t m_kind;
|
|
thread_buffer_t & m_thread_buffer;
|
|
char * m_stream_buffer;
|
|
};
|
|
|
|
const std::size_t STREAM_BUFFER_SIZE=4096;
|
|
|
|
exec_stream_buffer_t::exec_stream_buffer_t( exec_stream_t::stream_kind_t kind, thread_buffer_t & thread_buffer )
|
|
: m_kind( kind ), m_thread_buffer( thread_buffer )
|
|
{
|
|
m_stream_buffer=new char[STREAM_BUFFER_SIZE];
|
|
clear();
|
|
}
|
|
|
|
exec_stream_buffer_t::~exec_stream_buffer_t()
|
|
{
|
|
delete[] m_stream_buffer;
|
|
}
|
|
|
|
void exec_stream_buffer_t::clear()
|
|
{
|
|
if( m_kind==exec_stream_t::s_in ) {
|
|
setp( m_stream_buffer, m_stream_buffer+STREAM_BUFFER_SIZE );
|
|
}else {
|
|
setg( m_stream_buffer, m_stream_buffer+STREAM_BUFFER_SIZE, m_stream_buffer+STREAM_BUFFER_SIZE );
|
|
}
|
|
}
|
|
|
|
exec_stream_buffer_t::int_type exec_stream_buffer_t::underflow()
|
|
{
|
|
if( gptr()==egptr() ) {
|
|
std::size_t read_size=STREAM_BUFFER_SIZE;
|
|
bool no_more = true;
|
|
m_thread_buffer.get( m_kind, m_stream_buffer, read_size, no_more );
|
|
if( no_more || read_size==0 ) { // there is no way for underflow to return something other than eof when 0 bytes are read
|
|
return traits_type::eof();
|
|
}else {
|
|
setg( m_stream_buffer, m_stream_buffer, m_stream_buffer+read_size );
|
|
}
|
|
}
|
|
return traits_type::to_int_type( *eback() );
|
|
}
|
|
|
|
bool exec_stream_buffer_t::send_buffer()
|
|
{
|
|
if( pbase()!=pptr() ) {
|
|
std::size_t write_size=pptr()-pbase();
|
|
std::size_t n=write_size;
|
|
bool no_more;
|
|
m_thread_buffer.put( pbase(), n, no_more );
|
|
if( no_more || n!=write_size ) {
|
|
return false;
|
|
}else {
|
|
setp( m_stream_buffer, m_stream_buffer+STREAM_BUFFER_SIZE );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool exec_stream_buffer_t::send_char( char c )
|
|
{
|
|
std::size_t write_size=1;
|
|
bool no_more;
|
|
m_thread_buffer.put( &c, write_size, no_more );
|
|
return write_size==1 && !no_more;
|
|
}
|
|
|
|
exec_stream_buffer_t::int_type exec_stream_buffer_t::overflow( exec_stream_buffer_t::int_type c )
|
|
{
|
|
if( !send_buffer() ) {
|
|
return traits_type::eof();
|
|
}
|
|
if( c!=traits_type::eof() ) {
|
|
if( pbase()==epptr() ) {
|
|
if( !send_char( static_cast<char>(c) ) ) {
|
|
return traits_type::eof();
|
|
}
|
|
}else {
|
|
sputc( static_cast<char>(c) );
|
|
}
|
|
}
|
|
return traits_type::not_eof( c );
|
|
}
|
|
|
|
int exec_stream_buffer_t::sync()
|
|
{
|
|
if( !send_buffer() ) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// stream classes
|
|
|
|
class exec_istream_t : public std::istream {
|
|
public:
|
|
exec_istream_t( exec_stream_buffer_t & buf )
|
|
: std::istream( &buf ) {
|
|
}
|
|
};
|
|
|
|
|
|
class exec_ostream_t : public std::ostream {
|
|
public:
|
|
exec_ostream_t( exec_stream_buffer_t & buf )
|
|
: std::ostream( &buf ){
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
// platform-dependent implementation
|
|
#include IMPL_CPP
|
|
|
|
|
|
//platform-independent exec_stream_t member functions
|
|
exec_stream_t::exec_stream_t()
|
|
{
|
|
m_impl=new impl_t;
|
|
exceptions( true );
|
|
}
|
|
|
|
exec_stream_t::exec_stream_t( std::string const & program, std::string const & arguments )
|
|
{
|
|
m_impl=new impl_t;
|
|
exceptions( true );
|
|
start( program, arguments );
|
|
}
|
|
|
|
void exec_stream_t::new_impl()
|
|
{
|
|
m_impl=new impl_t;
|
|
}
|
|
|
|
exec_stream_t::~exec_stream_t()
|
|
{
|
|
try {
|
|
close();
|
|
}catch( ... ) {
|
|
}
|
|
delete m_impl;
|
|
}
|
|
|
|
std::ostream & exec_stream_t::in()
|
|
{
|
|
return m_impl->m_in;
|
|
}
|
|
|
|
std::istream & exec_stream_t::out()
|
|
{
|
|
return m_impl->m_out;
|
|
}
|
|
|
|
std::istream & exec_stream_t::err()
|
|
{
|
|
return m_impl->m_err;
|
|
}
|
|
|
|
void exec_stream_t::exceptions( bool enable )
|
|
{
|
|
if( enable ) {
|
|
// getline sets failbit on eof, so we should enable badbit and badbit _only_ to propagate our exceptions through iostream code.
|
|
m_impl->m_in.exceptions( std::ios_base::badbit );
|
|
m_impl->m_out.exceptions( std::ios_base::badbit );
|
|
m_impl->m_err.exceptions( std::ios_base::badbit );
|
|
}else {
|
|
m_impl->m_in.exceptions( std::ios_base::goodbit );
|
|
m_impl->m_out.exceptions( std::ios_base::goodbit );
|
|
m_impl->m_err.exceptions( std::ios_base::goodbit );
|
|
}
|
|
}
|
|
|
|
// exec_stream_t::error_t
|
|
namespace exec_stream_internal {
|
|
|
|
std::string int2str( unsigned long i, int base, std::size_t width )
|
|
{
|
|
std::string s;
|
|
s.reserve(4);
|
|
while( i!=0 ) {
|
|
s="0123456789abcdef"[i%base]+s;
|
|
i/=base;
|
|
}
|
|
if( width!=0 ) {
|
|
while( s.size()<width ) {
|
|
s="0"+s;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
}
|
|
|
|
exec_stream_t::error_t::error_t()
|
|
{
|
|
}
|
|
|
|
exec_stream_t::error_t::error_t( std::string const & msg )
|
|
{
|
|
m_msg=msg;
|
|
}
|
|
|
|
exec_stream_t::error_t::error_t( std::string const & msg, error_code_t code )
|
|
{
|
|
compose( msg, code );
|
|
}
|
|
|
|
exec_stream_t::error_t::~error_t() throw()
|
|
{
|
|
}
|
|
|
|
char const * exec_stream_t::error_t::what() const throw()
|
|
{
|
|
return m_msg.c_str();
|
|
}
|
|
|
|
|
|
void exec_stream_t::error_t::compose( std::string const & msg, error_code_t code )
|
|
{
|
|
m_msg=msg;
|
|
m_msg+="\n[code 0x"+int2str( code, 16, 4 )+" ("+int2str( code, 10, 0 )+")]";
|
|
}
|