///////////////////////////////////////////////////////////////////////////////
// MODULE: cdebug -- A C/C++ Debugger Interface for MuPAD
// AUTHOR: Andreas Sorgatz (Andreas.Sorgatz@sciface.com)
// DATE  : 24. Oct. 2003
///////////////////////////////////////////////////////////////////////////////

MMG( info      = "Module: C/C++ Debugger Interface" )
MMG( attribute = "static" )        // the module contains an interrupt handler!
MMG( generate  = "debug"  )        // support debugging of this dynamic module

#include "MTBproc.h"               // Module ToolBox for ´porcess´ support
#include "MTBfile.h"               // Module ToolBox for ´file´ support

// Flags to control the behaviour of the C/C++ debugger interface /////////////
static volatile int  CdebugActive      = 1;
static volatile int  CdebugNeverReturn = 0;
static volatile int  CdebugWaitForKey  = 1;

// Name (may be an absolute path) of the C/C++ source code debugger ///////////
static char  CdebugDebugger[MCpathMaxLen] = "ddd";

///////////////////////////////////////////////////////////////////////////////
// FUNC: CdebugInstallSignalHandlers
// ARGS: -
// DESC: Installs the specified signal handler to handle all serious signals.
///////////////////////////////////////////////////////////////////////////////
void CdebugInstallSignalHandlers( void (*handler)(int) )
{
    MFprocSetSignalHandler( SIGILL , handler );
    MFprocSetSignalHandler( SIGFPE , handler );
    MFprocSetSignalHandler( SIGSEGV, handler );
#ifndef WIN32
    MFprocSetSignalHandler( SIGBUS , handler );
    MFprocSetSignalHandler( SIGPIPE, handler );
#endif
#ifdef SOLARIS
    MFprocSetSignalHandler( SIGSYS , handler );
#endif
}

///////////////////////////////////////////////////////////////////////////////
// FUNC: CdebugStartDebugger
// ARGS: -
// DESC: Tries to start the C/C++ debugger.
///////////////////////////////////////////////////////////////////////////////
void CdebugStartDebugger( int error )
{
#ifdef WIN32
    MFprocFatalError( "\nFatal error..." );
#else
    char  mpid[32];
    char* args[4] = { CdebugDebugger, MVprocKernelName, mpid, NULL };
    sprintf( mpid, "%ld", MFprocId() );

    MFprocFatalError( "\nTrying to start debugger ´%s´...", CdebugDebugger );

    int  pid;
    if( (pid=fork()) == 0 ) {                           // new process branch
        osStopraw();                                    // reset the terminal
        execvp( args[0], args );                        // start the debugger
        MFprocFatalError( "Could not start the debugger.\n" ); // exec failed
        exit( 1 );
    } else if( pid == -1 ) {                            // fork failed
        MFprocFatalError( "Forking kernel for C/C++ debugger failed." );
        MFputs( "Forking kernel for C/C++ debugger failed." );
        if( error ) MFprocCatchError();                 // default handler
    } else {
        for( int i=0; i<10000; i++ );
        MFputs("#########################################################");
        MFputs("# Press any key to continue after quiting the debugger! #");
        MFputs("# or press Ctrl-Z to suspend the current MuPAD process. #");
        MFputs("#########################################################");
        //osStartraw();                            // and resets the terminal
        if( CdebugWaitForKey )
            osGetch();                             // child waits for any key
        if( CdebugNeverReturn == 1 ) {
            while( 1 ){};
        }
        MFputs( "Your back to the MuPAD session." );
    }
#endif
}

///////////////////////////////////////////////////////////////////////////////
// FUNC: CdebugInterruptHandler
// ARGS: -
// DESC: Interrupt handler which either starts the C/C++ debugger or calls
//       the default interrupt handler if debugging is set inactive.
///////////////////////////////////////////////////////////////////////////////
void CdebugInterruptHandler( int )
{
    CdebugInstallSignalHandlers( CdebugInterruptHandler );        // re-install
    if( CdebugActive )
        CdebugStartDebugger( 1 );
    else
        MFprocCatchError();                            // default error handler
}


///////////////////////////////////////////////////////////////////////////////
// Section: Module functions
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
MFUNC( initmod, MCnop )
{
    CdebugInstallSignalHandlers( CdebugInterruptHandler );        // do install

    MFprintf( "This module supports UNIX/Linux only.\n"
              "C/C++ Debugger interface is active.\n"
              "Debugger program: %s.\n"
              "Kernel processID: %ld.\n", CdebugDebugger, MFprocId() );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( new, MCnop )
{
    MFnargsCheck( 0 );

    CdebugStartDebugger( 0 );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( run, MCnop )
{
    MFnargsCheck( 0 );

    CdebugStartDebugger( 0 );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( setname, MCnop )
{
    MFnargsCheck( 1 );
    MFargCheck  ( 1, DOM_STRING );

    char*  newValue      = MFstring( MFarg(1) );
    if( strlen(newValue) >= MCpathMaxLen )
        MFerror( "Pathname is too long" );

    MTcell previousValue = MFstring( CdebugDebugger );
    strcpy( CdebugDebugger, newValue );
    MFprintf( "C/++ debugger name is set to ´%s´", CdebugDebugger );

    MFreturn( previousValue );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( returnon, MCnop )
{
    MFnargsCheck( 0 );

    CdebugNeverReturn = 0;
    MFprintf( "C/++ debugger will return to MuPAD." );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( returnoff, MCnop )
{
    MFnargsCheck( 0 );

    CdebugNeverReturn = 1;
    MFprintf( "C/++ debugger will never return to MuPAD." );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( waitkeyon, MCnop )
{
    MFnargsCheck( 0 );

    CdebugWaitForKey = 1;
    MFprintf( "MuPAD will wait for a pressed key after debugging." );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( waitkeyoff, MCnop )
{
    MFnargsCheck( 0 );

    CdebugWaitForKey = 0;
    MFprintf( "MuPAD will not wait for a pressed key after debugging." );

    MFreturn( MFcopy(MVnull) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( getpid, MCnop )
{
    MFnargsCheck( 0 );
    MFreturn( MFlong(MFprocId()) );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( status, MCnop )
{
    MFnargsCheck(0);

    MTcell status = MFnewList( 5 );

    MFsetList( &status, 0,
               MFstring(CdebugDebugger) );
    MFsetList( &status, 1,
               MFstring(CdebugActive ? "active": "inactive") );
    MFsetList( &status, 2,
               MFstring(CdebugNeverReturn ? "never return": "return") );
    MFsetList( &status, 3,
               MFstring(CdebugWaitForKey ? "wait key": "no wait") );
    MFsetList( &status, 4, MFlong(MFprocId()) );

    MFreturn( status );
} MFEND

///////////////////////////////////////////////////////////////////////////////
MFUNC( crash, MCnop )
{
    MFnargsCheck( 0 );
    CdebugActive      = 1;
    CdebugNeverReturn = 0;
    CdebugWaitForKey  = 1;

    char *null = NULL;
    *null = '?'; // crash

    MFputs( "This message should not appear due to an serious error." );
    MFreturn( MFcopy(MVnull) );
} MFEND
