/*
 * 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: siglink.cpp

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

#include <assert.h>
#include <signal.h>
#include "siglink.h"

/**************************/
/* STATIC INITIALIZATIONS */
/**************************/

SigLink*   SigLink::m_instance  = NULL;
Signal_map SigLink::m_signalhdl; 


/*
 * FUNCTION:    SigLink::~SigLink
 *
 * DESCRIPTION: Destructor
 *
 * IN:
 * IN-OUT:
 * RETURN CODE:
 */

SigLink::~SigLink ()
{

}


/*
 * FUNCTION:    SigLink::instance
 *
 * DESCRIPTION: This function creates an instance of SigLink if this one is
 *              not already existing, or returns a pointer to it otherwise
 *              (cf singleton).
 *
 * IN:
 * IN-OUT:
 * RETURN CODE: A pointer to THE SigLink object.
 */

SigLink* SigLink::instance ()
                throw (bad_alloc)
{
    if (m_instance == NULL)
        m_instance = new SigLink;
    
    return m_instance;
};

/*
 * FUNCTION:    SigLink::Register
 *
 * DESCRIPTION: This function registers a callback function with a signal
 *              inside the handlers attribute
 *
 * IN:          signo           The signal
 *              handler         The callback function
 *              arg             An optional argument passed to the callback
 * IN-OUT:
 * RETURN CODE:
 */

void SigLink::Register (int     signo, 
                        Handler handler,
                        void*   arg)
                throw (bad_alloc, SigLink_Exception)
{
    assert ((signo >= 0) && (handler != NULL));

    Handler_map* handlers;

    // Check if we have already a map corresponding to this signal
    if (m_signalhdl.find(signo) == m_signalhdl.end())
    {
        // No handler registered yet for this signal...
        handlers = new Handler_map;
        m_signalhdl.insert(make_pair(signo, handlers));

        // Insert signal handler
        handlers->insert(make_pair(handler, arg));
    }
    else
    {
        handlers = m_signalhdl[signo];

        if (handlers->find(handler) != handlers->end())
        {
            return; // Handler already registered!
        }

        handlers->insert(make_pair(handler, arg)); 
    }

    // Register the signal
    if (signal(signo, SigHandler) == SIG_ERR)
        throw(SigLink_Exception(SigLink_Exception::SIGLINK_SIGNAL_FAILED));

}

/*
 * FUNCTION:    SigLink::Deregister
 *
 * DESCRIPTION: This function de-registers a callback function associated
 *              with a signal.
 *
 * IN:          signo           The signal
 *              handler         The callback function
 * IN-OUT:
 * RETURN CODE:
 */

void SigLink::Deregister (int     signo, 
                          Handler handler)
                throw (SigLink_Exception)
{
    assert ((signo >= 0) && (handler != NULL));

    if (m_signalhdl.find(signo) == m_signalhdl.end())
    {
        throw(SigLink_Exception::SIGLINK_NO_SUCH_HANDLER);
        return;
    }

    Handler_map*  handlers;
    Handler_map::iterator  it;

    handlers = m_signalhdl[signo];

    if ((it = handlers->find(handler)) == handlers->end())
    {
        throw(SigLink_Exception::SIGLINK_NO_SUCH_HANDLER);
        return;
    }

    // Remove handler from list.
    handlers->erase(it);

    // Check if we still need to be notified for the signal
    if (handlers->empty() == true)
    {
        // remove handlers map from signal map
        m_signalhdl.erase(signo);
        signal(signo, SIG_IGN);
    }
}
    
/*
 * FUNCTION:    SigLink::SigHandler
 *
 * DESCRIPTION: This function calls all the handlers that have been registered
 *              for this particular signal
 *
 * IN:          signo           The signal
 * IN-OUT:
 * RETURN CODE:
 */

void SigLink::SigHandler (int signo)
                throw()
{
    signal(signo, SIG_IGN);

    // Check that at least one handler has registered for this signal!
    assert(m_signalhdl.find(signo) != m_signalhdl.end());

    Handler_map* handlers;
    Handler_map::iterator it;

    handlers = m_signalhdl[signo];

    // Call all the callbacks
    for (it = handlers->begin(); it != handlers->end(); it++)
        (*it).first(signo, (*it).second);
    
    signal(signo, SigLink::SigHandler);
}


/*
 * FUNCTION:    SigLink_Exception::what
 *
 * DESCRIPTION: This function returns a string description of an error
 *              code.
 *
 * IN:
 * IN-OUT:
 * RETURN CODE: A string description of the exception
 */

string SigLink_Exception::what () const
                throw ()
{
    string description("SigLink exception -> ");
    switch (m_error)
    {
    case SIGLINK_SIGNAL_FAILED:   
                        description.append("signal() failed");
                        break;
    case SIGLINK_NO_SUCH_HANDLER:
                        description.append("Signal handler not registered");
                        break;
    default:            description.append("No description");
                        break;
    }

    return description;
}


syntax highlighted by Code2HTML, v. 0.9.1