/*
 * freescope - Free source browser
 * Copyright (C) 2001  Olivier Deme
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

// FILE: trace.cpp

/************/
/* INCLUDES */
/************/

#ifndef NOT_BSD
#include <sys/time.h>
#endif // NOT_BSD

#include <stdio.h>
#include "trace.h"


/*******************/
/* INITIALIZATIONS */
/*******************/

      Trace::Flags  Trace::m_flags      = Trace::FLAGS_TIME_LOCAL;
      unsigned int  Trace::m_locLine    = 0;
      char*         Trace::m_locFile    = "";
      Trace::Module Trace::m_module     = 0;
      Trace::Level  Trace::m_level      = 0;
      Trace*        Trace::m_instance   = 0;
const Trace::Module Trace::GLOBAL       = 0;



/************************/
/* FUNCTION DEFINITIONS */
/************************/

/*
 * FUNCTION:    Trace::Trace
 *
 * DESCRIPTION: Constructor
 *
 * IN:
 * IN-OUT:
 * RETURN CODE:
 */

Trace::Trace () throw()
                : HEADER_DELIMITER('-'),
                  LEVEL_ERROR(-1),
                  m_start(true),
                  m_streams(OSTREAM_STDOUT),
                  m_moduleMaxLength(0),
                  m_levelMaxLength(0)
{
    for (unsigned int i = 0; i < sizeof(Module)*8; ++i)
    {
        m_modules[i].levels         = 0;
        m_modules[i].levDescrBits   = 0;
        m_modules[i].descriptionSet = false;
    }

    // Initialize a lookup table for level indexes
    int powerOfTwo = 1;
    for (unsigned int i = 0; i < sizeof(Level)*8; ++i)
    {
        m_lookupLevel[i]           = powerOfTwo;
        powerOfTwo                 *= 2;
    }

    m_modules[GLOBAL].descriptionSet = true;
    m_modules[GLOBAL].description    = "GLOBAL";
    cout.setf(ios::uppercase);
    cerr.setf(ios::uppercase);
}

/*
 * FUNCTION:    Trace::set_program_name
 *
 * DESCRIPTION: Set the program name
 *
 * IN:          name            The prohram name
 * IN-OUT:
 * RETURN CODE:
 */

void Trace::set_program_name ( const string& name)
                throw ()
{
    m_programName = name;
}

/*
 * FUNCTION:    Trace::add_to_mask
 *
 * DESCRIPTION: This function ORwises the bits passed as argument
 *              with the mask associated with the specified module.
 *
 * IN:          module          The module
 *              levels          The level(s) to add to the mask(s)
 * IN-OUT:
 * RETURN CODE: The old mask value
 */

Trace::Level Trace::add_to_mask (const Module module,
                                 const Level  levels)
                throw ()
{
    // Check module has valid value
    if ((size_t)module >= sizeof(Module)*8)
        return LEVEL_ERROR;

    Level old_mask = m_modules[module].levels;
    m_modules[module].levels |= levels;
    return old_mask;
}

/*
 * FUNCTION:    Trace::remove_from_mask
 *
 * DESCRIPTION: This function removes levels from the masks associated
 *              with the specified module.
 *
 * IN:          module          The module
 *              levels          The level(s) to remove from the mask(s)
 * IN-OUT:
 * RETURN CODE:
 */

Trace::Level Trace::remove_from_mask (const Module module,
                                      const Level  levels)
                throw ()
{
    // Check module has valid value
    if ((size_t)module >= sizeof(Module)*8)
        return LEVEL_ERROR;

    Level old_mask = m_modules[module].levels;
    m_modules[module].levels &= ~levels;
    return old_mask;
}

/*
 * FUNCTION:    Trace::set_mask
 *
 * DESCRIPTION: This function sets the mask for the specified module
 *
 * IN:          module          The module
 *              levels          The new mask
 * IN-OUT:
 * RETURN CODE:
 */

Trace::Level Trace::set_mask (const Module module,
                             const Level  levels)
                throw ()
{
    // Check module has valid value
    if ((size_t)module >= sizeof(Module)*8)
        return LEVEL_ERROR;

    Level old_mask = m_modules[module].levels;
    m_modules[module].levels = levels;
    return old_mask;
}

/*
 * FUNCTION:    Trace::set_level_description
 *
 * DESCRIPTION: This function sets the description for one or more
 *              specified levels
 *
 * IN:          module:         The affected module
 *              levels:         The affected levels
 *              description:    The description string
 * IN-OUT:
 * RETURN CODE:
 */

void Trace::set_level_description (const Module  module,
                                   const Level   levels,
                                   const string& description)
                throw()
{
    // Check module has valid value
    if ((size_t)module >= sizeof(Module)*8)
        return;

    Level level = 1;

    // Find affected levels
    for (unsigned int i = 0; i < sizeof(Level)*8; ++i)
    {
        if (levels & level)
        {
            // Set the description
            if (description.size() != 0)
            {
                m_modules[module].levDescr[i]   = description;
                m_modules[module].levDescrBits |= 0x1 << i;

                if (description.size() > m_levelMaxLength)
                    m_levelMaxLength = description.size();
            }
            else
            {
                // Unset description bit
                m_modules[module].levDescrBits &= ~(0x1 << i);

                m_levelMaxLength = 0;
                set_level_max_length();
            }
        }
        level <<= 1;
    }
}

/*
 * FUNCTION:    Trace::set_module_description
 *
 * DESCRIPTION: This function sets the decription for a module
 *
 * IN:          module          The module
 *              description     The description
 * IN-OUT:
 * RETURN CODE: 
 */

void Trace::set_module_description (const Module module,
                                    const string& description)
                throw()
{
    // Check module has valid value
    if ((size_t)module >= sizeof(Module)*8)
        return;

    if (description.size() != 0)
    {
        m_modules[module].description    = description;
        m_modules[module].descriptionSet = true;

        if (description.size() > m_moduleMaxLength)
            m_moduleMaxLength = description.size();

    }
    else
    {
        m_modules[module].descriptionSet = false;
        m_moduleMaxLength           = 0;
        set_module_max_length();
    }
}

/*
 * FUNCTION:    Trace::enable
 *
 * DESCRIPTION: This function ORwises the specified options with the 
 *              flags already set in the class
 *
 * IN:          flags           The flags
 * IN-OUT:
 * RETURN CODE: The old flags value
 */

Trace::Flags Trace::enable (const Flags flags)
                throw ()
{
    Flags old_flags = m_flags;
    m_flags |= flags;
    return old_flags;
}

/*
 * FUNCTION:    Trace::disable
 *
 * DESCRIPTION: This function disables the specified options from the class
 *              flags
 *
 * IN:          flags           The flags
 * IN-OUT:
 * RETURN CODE: The old flags value
 */

Trace::Flags Trace::disable (const Flags flags)
                throw ()
{
    Flags old_flags = m_flags;
    m_flags &= ~flags;
    return old_flags;
}

/*
 * FUNCTION:    Trace::set_flags
 *
 * DESCRIPTION: This function set the flags attribute.
 *
 * IN:          flags           The flags
 * IN-OUT:
 * RETURN CODE: The old flags value
 */

Trace::Flags Trace::set_flags (const Flags flags)
                throw ()
{
    Flags old_flags = m_flags;
    m_flags = flags;
    return old_flags;
}

/*
 * FUNCTION:    Trace::close
 *
 * DESCRIPTION: This function closes one or more streams
 *
 * IN:          streams         The streams to close
 * IN-OUT:
 * RETURN CODE: The resulting opened streams
 */

Trace::Stream Trace::close (Stream streams)
                throw()
{
    if ((m_streams & OSTREAM_FILE) && (streams & OSTREAM_FILE))
        m_file.close();
    
    m_streams &= ~streams;
    
    return m_streams;
}

/*
 * FUNCTION:    Trace::open
 *
 * DESCRIPTION: Open a stream
 *
 * IN:          streams         The streams to open
 * IN-OUT:
 * RETURN CODE: The resulting opened streams
 */

Trace::Stream Trace::open (Stream streams)
                throw ()
{
    // Ignore file stream beacuse no file name specified
    if (streams & OSTREAM_FILE)
        streams &= ~OSTREAM_FILE;

    m_streams |= streams;

    return m_streams;
}

/*
 * FUNCTION:    Trace::open
 *
 * DESCRIPTION: Open a stream
 *
 * IN:          streams         The streams to open
 *              filename        The name of a file to open
 * IN-OUT:
 * RETURN CODE: The resulting opened streams
 */

Trace::Stream Trace::open (const Stream  streams,
                           const string& filename)
{
    if ((streams & OSTREAM_FILE) &&
        ((m_streams & OSTREAM_FILE) == 0))
    {
        m_file.open(filename.c_str(), ios::app);
        if (m_file == 0)
            return m_streams;
    }

    m_file.setf(ios::uppercase);
    m_streams |= streams;
    return m_streams;
}

/*
 * FUNCTION:    Trace::create
 *
 * DESCRIPTION: This is the function that creates a Trace object
 *
 * IN:
 * IN-OUT:
 * RETURN CODE: true on success, false otherwise
 */

bool Trace::create ()
                throw (bad_alloc)
{
    assert(m_instance == 0);  // An instance has already been created!

    try
    {
        m_instance = new Trace();
    }
    catch (bad_alloc& ba)
    {
        // Propagate exception
        throw;
        return false;
    }

    return true;
}

/*
 * FUNCTION:    Trace::time_stamp()
 *
 * DESCRIPTION: This function gets the local time.
 *
 * IN:
 * IN-OUT:      buffer          The buffer used to store the time stamp
 * RETURN CODE: The number of characters written in the buffer
 */

int Trace::time_stamp (char* buffer)
                throw ()
{
    /*
     * UNIX-LIKE PLATFORMS 
     */

#if (!defined NO_GETTIMEOFDAY_AND_GETLOCALTIME && !defined HAVE_GETLOCALTIME)
    struct timeval t1;
    struct tm*     t2;

    gettimeofday(&t1, NULL);

    if (m_flags & FLAGS_TIME_LOCAL)
        t2 = localtime((time_t*)&t1.tv_sec);
    else
        t2 = gmtime((time_t*)&t1.tv_sec);

    sprintf(buffer,
            "%.2d:%.2d:%.2d.%.2d",
            t2->tm_hour,
            t2->tm_min,
            t2->tm_sec,
            (int)t1.tv_usec/10000);
    return 11;

    /*
     * WINDOWS PLATOFORMS
     */

#elif (defined HAVE_GETLOCATIME)

    /*
     * OTHER PLATFORMS
     */

#else // HAVE_GETLOCALTIME

    // Get the time with a resolution of a second
    struct time_t  t1;
    struct tm*     t2;

    time(&t1);
    if (m_flags & FLAGS_TIME_LOCAL)
        t2 = localtime(&t1);
    else
        t2 = gmtime(&t1);

    sprintf(buffer,
            "%.2d:%.2d:%.2d",
            t2->tm_hour,
            t2->tm_min,
            t2->tm_sec);
    return 8;

#endif // !NO_GETTIMEOFDAY_AND_GETLOCALTIME && !HAVE_GETLOCALTIME

    return 0;
}

/*
 * FUNCTION:    Trace::do_header
 *
 * DESCRIPTION: This function outputs to the opened streams all the 
 *              header fields that have been enabled.
 *
 * IN:
 * IN-OUT:
 * RETURN CODE:
 */

void Trace::do_header ()
{
    // Use character buffer for efficiency reasons
    char  header[80];
    char* p = header;

    header[0] = '\0';

    if ((m_flags & FLAGS_PROGRAM_NAME) &&
        (m_programName.size() != 0))
    {
        p += sprintf(p,
                     "%s %c ",
                     m_programName.c_str(),
                     HEADER_DELIMITER);
    }

    if (m_flags & FLAGS_TIMESTAMP)
    {
        p += time_stamp(p);
        p += sprintf(p, " %c ", HEADER_DELIMITER);
    }

    if (m_flags & FLAGS_MODULE_DESCRIPTION)
    {
        if (m_modules[m_module].descriptionSet == true)
        {
            if (m_flags & FLAGS_ALIGNMENT)
            {
                p += sprintf(p,
                             "%-*s %c ",
                             m_moduleMaxLength,
                             m_modules[m_module].description.c_str(),
                             HEADER_DELIMITER);
            }
            else
            {
                p += sprintf(p,
                             "%s %c ",
                             m_modules[m_module].description.c_str(),
                             HEADER_DELIMITER);
            }
        }
    }

    if (m_flags & FLAGS_LEVEL_DESCRIPTION)
    {
        int indexLevel;

        // Retrieve level index.
        for (indexLevel = 0;
             !(m_lookupLevel[indexLevel] & m_level);
             ++indexLevel);

        if (m_modules[m_module].levDescrBits & (0x1 << indexLevel))
        {
            // A description has ben provided for this level
            if (m_flags & FLAGS_ALIGNMENT)
            {
                p += sprintf(p,
                             "%-*s %c ",
                             m_levelMaxLength,
                             m_modules[m_module].levDescr[indexLevel].c_str(),
                             HEADER_DELIMITER);
            }
            else
            {
                p += sprintf(p,
                             "%s %c ",
                             m_modules[m_module].levDescr[indexLevel].c_str(),
                             HEADER_DELIMITER);
            }

        }
        else
        {
            // No description has been provided for this level.
            if (m_flags & FLAGS_ALIGNMENT)
            {
                p += sprintf(p,
                             "0x%-*X %c ",
                             m_levelMaxLength,
                             m_level,
                             HEADER_DELIMITER);
            }
            else
            {
                p += sprintf(p,
                             "0x%X %c ",
                             m_level,
                             HEADER_DELIMITER);
            }

        }
    }

    if (m_flags & FLAGS_CALLER)
    {
        p += sprintf(p, 
                     "%s:%d %c ",
                     m_locFile,
                     m_locLine,
                     HEADER_DELIMITER);
    }


    // Send the header to the opened streams
    if (m_streams & OSTREAM_STDOUT)
        cout << header;
    if (m_streams & OSTREAM_STDERR)
        cerr << header;
    if (m_streams & OSTREAM_FILE)
        m_file << header;

    // do not print the header until "endl" or "flush" manipulators
    m_start = false;
}

/*
 * FUNCTION:    Trace::set_module_max_length
 *
 * DESCRIPTION: Retrieve and store the biggest length among all module
 *              descriptions.
 *
 * IN:
 * IN-OUT:
 * RETURN CODE:
 */

void Trace::set_module_max_length ()
                throw ()
{
    for (unsigned int i = 0; i < sizeof(Module)*8; ++i)
    {
        if ((m_modules[i].descriptionSet == true) &&
            (m_modules[i].description.size() > m_moduleMaxLength))
        {
            m_moduleMaxLength = m_modules[i].description.size();
        }
    }
}

/*
 * FUNCTION:    Trace::set_level_max_length
 *
 * DESCRIPTION: Retrieve and store the biggest length among all level
 *              descriptions.
 *
 * IN:
 * IN-OUT:
 * RETURN CODE:
 */

void Trace::set_level_max_length ()
                throw ()
{
    for (unsigned int i = 0; i < sizeof(Module)*8; ++i)
    {
        Level level = 0x1;

        // Iterate levels
        for (unsigned int j = 0; j < sizeof(Level)*8; ++j)
        {
            if ((m_modules[i].levDescrBits & level) &&
                (m_modules[i].levDescr[j].size() > m_levelMaxLength))
            {
                m_levelMaxLength = m_modules[i].levDescr[j].size();
            }
            level <<= 1;
        }
    }
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a character to the streams
 *
 */

Trace& Trace::operator<< (char character)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;
    
    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << character;
    if (m_streams & OSTREAM_STDERR)
        cerr << character;
    if (m_streams & OSTREAM_FILE)
        m_file << character;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a character to the streams
 *
 */

Trace& Trace::operator<< (unsigned char character)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << character;
    if (m_streams & OSTREAM_STDERR)
        cerr << character;
    if (m_streams & OSTREAM_FILE)
        m_file << character;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a string to the streams
 *
 */

Trace& Trace::operator<< (char* str)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << str;
    if (m_streams & OSTREAM_STDERR)
        cerr << str;
    if (m_streams & OSTREAM_FILE)
        m_file << str;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a string to the streams
 *
 */

Trace& Trace::operator<< (unsigned char* str)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << str;
    if (m_streams & OSTREAM_STDERR)
        cerr << str;
    if (m_streams & OSTREAM_FILE)
        m_file << str;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a string to the streams
 *
 */

Trace& Trace::operator<< (const string& str)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << str.c_str();
    if (m_streams & OSTREAM_STDERR)
        cerr << str.c_str();
    if (m_streams & OSTREAM_FILE)
        m_file << str.c_str();

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send an int to the streams
 *
 */

Trace& Trace::operator<< (int nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send an unsigned int to the streams
 *
 */

Trace& Trace::operator<< (unsigned int nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a long to the streams
 *
 */

Trace& Trace::operator<< (long nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send an unsigned long to the streams
 *
 */

Trace& Trace::operator<< (unsigned long nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a short to the streams
 *
 */

Trace& Trace::operator<< (short nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send an unsigned short to the streams
 *
 */

Trace& Trace::operator<< (unsigned short nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a float short to the streams
 *
 */

Trace& Trace::operator<< (float nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    Trace::operator<<
 *
 * DESCRIPTION: Send a double short to the streams
 *
 */

Trace& Trace::operator<< (double nbr)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    if (m_start == true)
        do_header();
    
    if (m_streams & OSTREAM_STDOUT)
        cout << nbr;
    if (m_streams & OSTREAM_STDERR)
        cerr << nbr;
    if (m_streams & OSTREAM_FILE)
        m_file << nbr;

    return *this;
}

/*
 * OPERATOR:    operator<<
 *
 * DESCRIPTION: This operator is used for implementing the Hex effector.
 */

Trace& operator<< (Trace& trace, const Hex& h)
{
    // Do nothing if module is invalid
    if ((size_t)trace.m_module >= sizeof(Trace::Module)*8)
        return trace;

    if ((trace.m_modules[trace.m_module].levels & trace.m_level) == 0)
        return trace;

    if (trace.m_start == true)
        trace.do_header();
    
    if (trace.m_streams & Trace::OSTREAM_STDOUT)
    {
        cout.setf(ios::hex, ios::basefield);
        cout << "0x" << h.m_nbr;
        cout.setf(ios::dec, ios::basefield);
    }
    if (trace.m_streams & Trace::OSTREAM_STDERR)
    {
        cerr.setf(ios::hex, ios::basefield);
        cerr << "0x" << h.m_nbr;
        cerr.setf(ios::dec, ios::basefield);
    }
    if (trace.m_streams & Trace::OSTREAM_FILE)
    {
        trace.m_file.setf(ios::hex, ios::basefield);
        trace.m_file << "0x" << h.m_nbr;
        trace.m_file.setf(ios::dec, ios::basefield);
    }

    return trace;
}

Trace& Trace::operator<< (Trace_manip m)
{
    // Do nothing if module is invalid
    if ((size_t)m_module >= sizeof(Module)*8)
        return *this;

    if ((m_modules[m_module].levels & m_level) == 0)
        return *this;

    m(*this);
    return *this;
}

Trace& operator<< (Trace& trace, const Width& w)
{
    // Do nothing if module is invalid
    if ((size_t)trace.m_module >= sizeof(Trace::Module)*8)
        return trace;

    if ((trace.m_modules[trace.m_module].levels & trace.m_level) == 0)
        return trace;

    int justify;

    if (trace.m_start == true)
        trace.do_header();
    
    switch (w.m_fmt)
    {
    case Width::LEFT:  justify = ios::left;
                       break;
    case Width::RIGHT: justify = ios::right;
                       break;
    default:           return trace;
    }

    if (trace.m_streams & Trace::OSTREAM_STDOUT)
    {
        cout.width(w.m_width);
        cout.fill(w.m_fill);
        cout.setf(justify, ios::adjustfield);
    }
    if (trace.m_streams & Trace::OSTREAM_STDERR)
    {
        cerr.width(w.m_width);
        cerr.fill(w.m_fill);
        cerr.setf(justify, ios::adjustfield);
    }
    if (trace.m_streams & Trace::OSTREAM_FILE)
    {
        trace.m_file.width(w.m_width);
        trace.m_file.fill(w.m_fill);
        trace.m_file.setf(justify, ios::adjustfield);
    }

    return trace;
}

/*
 * MANIPULATOR: endl
 *
 * DESCRIPTION: This manipulator adds a carriage return and flushes the
 *              opened streams.
 */

Trace& endl (Trace& trace)
{
    // Do nothing if module is invalid
    if ((size_t)trace.m_module >= sizeof(Trace::Module)*8)
        return trace;

    if ((trace.m_modules[trace.m_module].levels & trace.m_level) == 0)
        return trace;

    if (trace.m_start == true)
        trace.do_header();
    
    if (trace.m_streams & Trace::OSTREAM_STDOUT)
        cout << endl;
    if (trace.m_streams & Trace::OSTREAM_STDERR)
        cerr << endl;
    if (trace.m_streams & Trace::OSTREAM_FILE)
        trace.m_file << endl;

    trace.m_start = true;
    return trace;
}

/*
 * MANIPULATOR: flush
 *
 * DESCRIPTION: This manipulator flushes the opened streams
 */

Trace& flush (Trace& trace)
{
    // Do nothing if module is invalid
    if ((size_t)trace.m_module >= sizeof(Trace::Module)*8)
        return trace;

    if ((trace.m_modules[trace.m_module].levels & trace.m_level) == 0)
        return trace;

    if (trace.m_start == true)
        trace.do_header();

    if (trace.m_streams & Trace::OSTREAM_STDOUT)
        cout << flush;
    if (trace.m_streams & Trace::OSTREAM_STDERR)
        cerr << flush;
    if (trace.m_streams & Trace::OSTREAM_FILE)
        trace.m_file << flush;

    trace.m_start = true;
    return trace;
}

/*
 * FUNCTION:    Width::Width
 *
 * DESCRIPTION: Effector constructor
 *
 * IN:          justify         The type of justification
 *              width           The width of the next output
 *              fill            The filling character
 * IN-OUT:
 * RETURN CODE:
 */

Width::Width (Format justify, unsigned int width, char fill)
                : m_fmt(justify),
                  m_width(width),
                  m_fill(fill)
{
}


syntax highlighted by Code2HTML, v. 0.9.1