Listing 2: Implementation of Errstream class

#include <stdio.h>
#include <unistd.h>
#include "Errstream.h"

Errstream errout;

static void
_StdDisplayImpl_(const char *msg)
{
    cerr << msg << endl;
}

void
(*Errstream::_displayImpl)(const char *)
    = _StdDisplayImpl_;

Errstream::Errstream()
    : _line(0),
      _collectLine(0),
      _diffLevel(0),
      _collectDiffLevel(0),
      _numMultiples(0),
      _collect(true),
      _logOnly(false),
      _logOpen(false)
{
    clear();
    pid_t pid = getpid();
    sprintf(_logFile, "%s.%d",
        DEFAULT_ERROR_FILE, (int)pid);
}

Errstream::Errstream(const char *name)
    : _line(0),
      _collectLine(0),
      _diffLevel(0),
      _collectDiffLevel(0),
      _numMultiples(0),
      _collect(true),
      _logOnly(false),
      _logOpen(false)
{
    clear();
    strcpy(_logFile, name);
}

Errstream::~Errstream()
{
    if(_collect && _numMultiples)
        _writeMultiples();
    ofstream::close();
}

void Errstream::open(const char *name)
{
    ofstream::close();
    ofstream::open(name);
}

void Errstream::collect(bool set)
{
    if(_numMultiples && !set)
        _writeMultiples();
    _collect = set;
}

Errstream &
Errstream::displayError(Errstream &stream)
{
    if(!stream.logOnly())
       (*_displayImpl)(stream.errString());
    if(stream._collectMultipleMsgs())
        return stream;
    stream._writeMultiples();
    stream._writeDebugInfo();
    return stream;
}

Errstream &
Errstream::operator<<(const char *str)
{
    _concatenate(str);
    if(_okayToWriteToLog())
        (ofstream&)*this << str;
    return *this;
}

Errstream &Errstream::operator<<(int d)
{
    char buf[64];
    sprintf(buf, "%d", d);
    _concatenate(buf);
    if(_okayToWriteToLog())
        (ofstream&)*this << d;
    return *this;
}

Errstream &Errstream::operator<<(double f)
{
    char buf[64];
    sprintf(buf, "%lf", f);
    _concatenate(buf);
    if(_okayToWriteToLog())
        (ofstream&)*this << f;
    return *this;
}

void Errstream::_openLogFile()
{
    ofstream::open(_logFile);
    if(good())
        _logOpen = true;
    else
        cerr << "Unable to open log file "
             << _logFile << endl;
}

bool Errstream::_collectMultipleMsgs()
{
    if(!_collect ||
       (_diffLevel != _collectDiffLevel) ||
       (_line != _collectLine) ||
       (_file != _collectFile))
        return false;
    clear();
    _logOnly = false;
    _diffLevel = 0;
    _numMultiples++;
    return true;
}

void Errstream::_writeMultiples()
{
    if(_numMultiples)
    {
        (ofstream&)*this
           << "\n\t*** MESSAGE REPEATS "
           << _numMultiples
           << " TIMES ***\n"
           << endl;
        (ofstream&)*this << _str;
        _numMultiples = 0;
    }
}

void Errstream::_writeDebugInfo()
{
    if(_collect)
    {
        _collectLine = _line;
        _collectFile = _file;
        _collectDiffLevel = _diffLevel;
    }
    clear();
    _logOnly = false;
    _diffLevel = 0;
    (ofstream&)*this << "\n\tFile: "
                     << _file
                     << "\n\tLine: "
                     << _line
                     << '\n'
                     << endl;
}

void
Errstream::_concatenate
   (const char *buf, int len)
{
    int catSize =
MAX_STR_SIZE - strlen(_str);
    if(catSize > len)  catSize = len;
    strncat(_str, buf, catSize);
}

bool Errstream::_okayToWriteToLog()
{
    if(_collect && _numMultiples)
        return false;
    if(!_logOpen)
        _openLogFile();
    return true;
}

Errstream &
set_diffLevel(Errstream &stream, int d)
{
    stream.diffLevel(d);
    return stream;
}

Errstream &
set_debug_line(Errstream &stream, int l)
{
    stream.line(l);
    return stream;
}

Errstream &
set_debug_file(Errstream &stream,
    const std::string &f)
{
    stream.file(f);
    return stream;
}

Errstream &logOnly(Errstream &stream)
{
    stream.logOnly(true);
    return stream;
}

Errstream &endl(Errstream &stream)
{
    (ofstream&)stream << endl;
    return stream;
}

EMANIP_int differentiate(int d)
{
    return EMANIP_int(set_diffLevel, d);
}

EMANIP_int setDebugLine(int l)
{
    return EMANIP_int(set_debug_line, l);
}

EMANIP_string
setDebugFile(const std::string &f)
{
    return EMANIP_string(set_debug_file,
               f);
}