303 lines
9.7 KiB
C++
303 lines
9.7 KiB
C++
// stdiostream.hpp
|
|
//
|
|
// Copyright (c) 2010 Michael Thomas Greer
|
|
// Distributed under the Boost Software License, Version 1.0
|
|
// (See http://www.boost.org/LICENSE_1_0.txt )
|
|
//
|
|
|
|
#pragma once
|
|
#ifndef DUTHOMHAS_STDIOSTREAM_HPP
|
|
#define DUTHOMHAS_STDIOSTREAM_HPP
|
|
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <streambuf>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace duthomhas
|
|
{
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
basic_stdiobuf
|
|
///////////////////////////////////////////////////////////////////////// */
|
|
|
|
template <
|
|
typename CharType,
|
|
typename CharTraits = std::char_traits <CharType>
|
|
>
|
|
class basic_stdiobuf: public std::basic_streambuf <CharType, CharTraits>
|
|
{
|
|
//------------------------------------------------------------------------
|
|
public:
|
|
//------------------------------------------------------------------------
|
|
typedef CharType char_type;
|
|
typedef CharTraits traits_type;
|
|
typedef typename traits_type::int_type int_type;
|
|
typedef typename traits_type::pos_type pos_type;
|
|
typedef typename traits_type::off_type off_type;
|
|
|
|
typedef basic_stdiobuf <char_type, traits_type> this_type;
|
|
|
|
//......................................................................
|
|
basic_stdiobuf( FILE* fp = NULL ):
|
|
fp( fp )
|
|
{ }
|
|
|
|
//......................................................................
|
|
//BUG 1: Hey! I never get called! (How is that?)
|
|
~basic_stdiobuf()
|
|
{
|
|
this->close();
|
|
}
|
|
|
|
//......................................................................
|
|
bool is_open() const throw()
|
|
{
|
|
return fp != NULL;
|
|
}
|
|
|
|
//......................................................................
|
|
this_type* open( const char* filename, std::ios_base::openmode mode )
|
|
{
|
|
if (is_open()) return NULL;
|
|
|
|
// Figure out the open mode flags . . . . . . . . . . . . . . . . . .
|
|
std::string fmode;
|
|
|
|
bool is_ate = mode & std::ios_base::ate;
|
|
bool is_bin = mode & std::ios_base::binary;
|
|
mode &= ~(std::ios_base::ate | std::ios_base::binary);
|
|
|
|
#define _(flag) std::ios_base::flag
|
|
if (mode == ( _(in) )) fmode = "r";
|
|
else if (mode == ( _(out) & _(trunc))) fmode = "w";
|
|
else if (mode == (_(app) & _(out) )) fmode = "a";
|
|
else if (mode == ( _(in) & _(out) )) fmode = "r+";
|
|
else if (mode == ( _(in) & _(out) & _(trunc))) fmode = "w+";
|
|
else if (mode == (_(app) & _(in) & _(out) )) fmode = "a+";
|
|
// I would prefer to throw an exception here,
|
|
// but the standard only wants a NULL result.
|
|
else return NULL;
|
|
#undef _
|
|
if (is_bin) fmode.insert( 1, 1, 'b' );
|
|
|
|
// Try opening the file . . . . . . . . . . . . . . . . . . . . . . .
|
|
fp = std::fopen( filename, fmode.c_str() );
|
|
if (!fp) return NULL;
|
|
|
|
// Reposition to EOF if wanted . . . . . . . . . . . . . . . . . . . .
|
|
if (is_ate) std::fseek( fp, 0, SEEK_END );
|
|
|
|
return this;
|
|
}
|
|
|
|
//......................................................................
|
|
this_type* close()
|
|
{
|
|
if (fp)
|
|
{
|
|
std::fclose( fp );
|
|
fp = NULL;
|
|
}
|
|
pushbacks.clear();
|
|
return this;
|
|
}
|
|
|
|
//......................................................................
|
|
FILE* stdiofile() const
|
|
{
|
|
return fp;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
protected:
|
|
//------------------------------------------------------------------------
|
|
|
|
//......................................................................
|
|
// Get the CURRENT character without advancing the file pointer
|
|
virtual int_type underflow()
|
|
{
|
|
// Return anything previously pushed-back
|
|
if (pushbacks.size())
|
|
return pushbacks.back();
|
|
|
|
// Else do the right thing
|
|
fpos_t pos;
|
|
if (std::fgetpos( fp, &pos ) != 0)
|
|
return traits_type::eof();
|
|
|
|
int c = std::fgetc( fp );
|
|
std::fsetpos( fp, &pos );
|
|
|
|
return maybe_eof( c );
|
|
}
|
|
|
|
//......................................................................
|
|
// Get the CURRENT character AND advance the file pointer
|
|
virtual int_type uflow()
|
|
{
|
|
// Return anything previously pushed-back
|
|
if (pushbacks.size())
|
|
{
|
|
int_type c = pushbacks.back();
|
|
pushbacks.pop_back();
|
|
return c;
|
|
}
|
|
|
|
// Else do the right thing
|
|
return maybe_eof( std::fgetc( fp ) );
|
|
}
|
|
|
|
//......................................................................
|
|
virtual int_type pbackfail( int_type c = traits_type::eof() )
|
|
{
|
|
if (!is_open())
|
|
return traits_type::eof();
|
|
|
|
// If the argument c is EOF and the file pointer is not at the
|
|
// beginning of the character sequence, it is decremented by one.
|
|
if (traits_type::eq_int_type( c, traits_type::eof() ))
|
|
{
|
|
pushbacks.clear();
|
|
return std::fseek( fp, -1L, SEEK_CUR )
|
|
? traits_type::eof()
|
|
: 0;
|
|
}
|
|
|
|
// Otherwise, make the argument the next value to be returned by
|
|
// underflow() or uflow()
|
|
pushbacks.push_back( c );
|
|
return c;
|
|
}
|
|
|
|
//......................................................................
|
|
virtual int_type overflow( int_type c = traits_type::eof() )
|
|
{
|
|
pushbacks.clear();
|
|
|
|
// Do nothing
|
|
if (traits_type::eq_int_type( c, traits_type::eof() ))
|
|
return 0;
|
|
|
|
// Else write a character
|
|
return maybe_eof( std::fputc( c, fp ) );
|
|
}
|
|
|
|
//......................................................................
|
|
virtual this_type* setbuf( char* s, std::streamsize n )
|
|
{
|
|
return std::setvbuf( fp, s, (s and n) ? _IOLBF : _IONBF, (size_t)n )
|
|
? NULL
|
|
: this;
|
|
}
|
|
|
|
//......................................................................
|
|
virtual pos_type seekoff(
|
|
off_type offset,
|
|
std::ios_base::seekdir direction,
|
|
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out
|
|
) {
|
|
pushbacks.clear();
|
|
return std::fseek( fp, offset,
|
|
(direction == std::ios_base::beg) ? SEEK_SET :
|
|
(direction == std::ios_base::cur) ? SEEK_CUR :
|
|
SEEK_END
|
|
) ? (-1) : std::ftell( fp );
|
|
}
|
|
|
|
//......................................................................
|
|
virtual pos_type seekpos(
|
|
pos_type position,
|
|
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out
|
|
) {
|
|
pushbacks.clear();
|
|
return std::fseek( fp, position, SEEK_SET )
|
|
? (-1)
|
|
: std::ftell( fp );
|
|
}
|
|
|
|
//......................................................................
|
|
virtual int sync()
|
|
{
|
|
pushbacks.clear();
|
|
return std::fflush( fp )
|
|
? traits_type::eof()
|
|
: 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
private:
|
|
//------------------------------------------------------------------------
|
|
FILE* fp;
|
|
std::vector <int_type> pushbacks; // we'll treat this like a stack
|
|
|
|
//......................................................................
|
|
// Utility function to make sure EOF gets translated to the proper value
|
|
inline int_type maybe_eof( int value ) const
|
|
{
|
|
return (value == EOF) ? traits_type::eof() : value;
|
|
}
|
|
};
|
|
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
basic_stdiostream
|
|
///////////////////////////////////////////////////////////////////////// */
|
|
|
|
template <
|
|
typename CharType,
|
|
typename CharTraits = std::char_traits <CharType>
|
|
>
|
|
struct basic_stdiostream: public std::basic_iostream <CharType, CharTraits>
|
|
{
|
|
typedef CharType char_type;
|
|
typedef CharTraits traits_type;
|
|
|
|
typedef basic_stdiobuf <char_type, traits_type> sbuf_type;
|
|
typedef basic_stdiostream <char_type, traits_type> this_type;
|
|
typedef std::basic_iostream <char_type, traits_type> base_type;
|
|
|
|
//......................................................................
|
|
basic_stdiostream( FILE* fp = NULL ):
|
|
base_type( new sbuf_type( fp ) )
|
|
{ }
|
|
|
|
//......................................................................
|
|
basic_stdiostream( const char* filename, std::ios_base::openmode mode ):
|
|
//BUG 2: Oops! This is a potential memory leak!
|
|
base_type( (new sbuf_type)->open( filename, mode ) )
|
|
{ }
|
|
|
|
//......................................................................
|
|
void open(
|
|
const char* filename,
|
|
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out
|
|
) {
|
|
sbuf_type* buf = static_cast <sbuf_type*> ( this->rdbuf() );
|
|
if (!(buf->open( filename, mode )))
|
|
this->setstate( std::ios_base::badbit );
|
|
}
|
|
|
|
//......................................................................
|
|
void close()
|
|
{
|
|
sbuf_type* buf = static_cast <sbuf_type*> ( this->rdbuf() );
|
|
buf->close();
|
|
}
|
|
};
|
|
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
Useful typedefs
|
|
///////////////////////////////////////////////////////////////////////// */
|
|
|
|
typedef basic_stdiobuf <char> stdiobuf;
|
|
typedef basic_stdiostream <char> stdiostream;
|
|
|
|
} // namespace duthomhas
|
|
|
|
#endif
|
|
|
|
// end stdiostream.hpp
|