/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1998-2000 Microsoft Corporation

 Module Name:

    netmon.cxx

 Abstract:

    Generate C code for the stub DLL's used for Netmon debugging

 Notes:

    Two files are generated by this file:  
        xxx_netmon_stub.c
        xxx_netmob_stub_obj.c


 History:

    - Created 7/28/98 by Max Attar Feingold
 ----------------------------------------------------------------------------*/

#include "becls.hxx"

// Stub version
#define NETMON_STUB_VERSION "(float) 1.0"

#pragma hdrstop

CG_STATUS
CG_NETMONSTUB_FILE::GenCode(
    CCB     *   pCCB)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the Netmon stub file for classic and object interfaces

 Arguments:

    pCCB    - The code gen controller block.
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
{

    ISTREAM             Stream( GetFileName(), 4 );
    ISTREAM *           pStream = pCCB->SetStream( &Stream, this );

    m_pCCB = pCCB;
    m_pStream = pStream;

    // Scan for interfaces of the appropriate type
    ScanInterfaces();
    if (!m_bDoObject && !m_bClassic ||
        m_bDoObject && !m_bObject) {
        m_pStream->Close();
        return CG_OK;
    }

    // If m_bDoObject is TRUE, then we're producing a stub file for object interfaces;
    // otherwise, we're doing classic interfaces
    if (m_bDoObject) {
        EmitFileHeadingBlock (pCCB, "code for an object interface Netmon stub DLL", 
            "This file should be compiled as source for a DLL, linked with rpcrt4.lib");
    } else {
        EmitFileHeadingBlock (pCCB, "code for a classic interface Netmon stub DLL", 
            "This file should be compiled as source for a DLL, linked with rpcrt4.lib");
    }

    // Write standard include files
    EmitStandardIncludes();

    // Write local include files
    EmitLocalIncludes();

    // Write definitions (#defines and variables needed by the code)
    EmitDefinitions();

    if (m_bDoObject) {
        
        // We seem to need this to solve a link error
        OpenComment();
        EmitComment ("This implementation is needed to solve a link error");
        CloseComment();
        pStream->NewLine();
        pStream->Write ("ULONG STDMETHODCALLTYPE CStdStubBuffer_Release "\
            "(IRpcStubBuffer *This) { ULONG u = 0; return u; }");
        
        // Write the special data for object interfaces
        EmitObjectInterfaceData();
    }

    // Write the server and client debug procedures for each interface
    EmitDebugProcedures();

    // Write the data tables
    EmitDataTables();

    // Close the header block
    EmitFileClosingBlock( pCCB );

    // Close the stream
    pStream->Close();

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Descriptions:

    Determine which types of interfaces (object or classic) we have and how many,
    adding the names to the interface table

 Arguments:
    
 Return Value:

    TRUE if yes
    FALSE if no
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::ScanInterfaces() {
    
    CG_ITERATOR I;
    CG_NDR * pCG;
    
    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) )
    {
		// We check GetChild()'s non-nullness here and elsewhere in the code because
		// there are object interfaces without children that cause problems, such as IUnknown
        if (pCG->GetChild()) {
            switch(pCG->GetCGID())
            {
            case ID_CG_INTERFACE:
                m_bClassic = TRUE;
                m_lNumClassicInterfaces ++;
                m_itInterfaceTable.AddInterface (((CG_INTERFACE*)pCG)->GetInterfaceName());
                break;
                
            case ID_CG_OBJECT_INTERFACE:
                m_bObject = TRUE;
                m_lNumObjectInterfaces ++;
                m_itInterfaceTable.AddInterface (((CG_INTERFACE*)pCG)->GetInterfaceName());
                break;
                
            default:
                break;
            }
        }
    }

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Descriptions:

    Respectively,
    -Open a comment block
    -Write comments
    -Close a comment block

 Arguments:
    
      pszString is the comment that should be emitted

 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::OpenComment() {
    
    m_pStream->NewLine();
    m_pStream->NewLine();
    m_pStream->Write ("/*");

    return CG_OK;
}

CG_STATUS CG_NETMONSTUB_FILE::EmitComment (char* pszString) {
    
    m_pStream->NewLine();
    m_pStream->Write (" * ");
    m_pStream->Write (pszString);

    return CG_OK;
}

CG_STATUS CG_NETMONSTUB_FILE::CloseComment() {
    
    m_pStream->NewLine();
    m_pStream->Write (" */");

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the standard includes for the netmon stub dll

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitStandardIncludes() {

	m_pStream->NewLine();
    m_pStream->Write ("#include <stdio.h>");

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the local includes for the netmon stub dll.

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

    Local includes are the proxy and iid files for object interfaces
    and the stub file for classic interfaces.

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitLocalIncludes() {

    m_pStream->NewLine(2);
    
    // This is defined to avoid target errors
    m_pStream->Write ("#define _WIN32_DCOM");
    
    // Write includes
    m_pStream->NewLine();
    if (m_bDoObject) {
        m_pStream->Write ("#include \"");
        m_pStream->Write (pCommand->GetProxyFName());
        m_pStream->Write ("\"");
        m_pStream->NewLine();
        m_pStream->Write ("#include \"");
        m_pStream->Write (pCommand->GetIIDFName());
        m_pStream->Write ("\"");
        m_pStream->NewLine();
    } else {
        m_pStream->Write ("#include \"");
        m_pStream->Write (pCommand->GetSstubFName());
        m_pStream->Write ("\"");
        m_pStream->NewLine();
    }

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate any definitions needed by the netmon stub dll.

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
const char* ppszIncludeBlock[] = {
    "#define DLL_EXPORT_PROC(x) __declspec(dllexport) GLOBAL_INTERFACE_DATA* GetGlobalInterfaceData__##x () { return &GlobalInterfaceData__##x ; }",
    "#define NetmonStubAllocate(x) malloc(x)",
    "#define Debug() *pNull = 0",
    "static DWORD* pNull = NULL;",
    NULL
};

const char* ppszGlobalInterfaceDataStructure[] = {
	"float Version;",
    "char* InterfaceName;",
    "DWORD ProcFormatStringSize;",
    "DWORD NumProcedures;",
    "char** ProcedureNames;",
    "SERVER_ROUTINE* ServerRoutineTable;",
    "SERVER_ROUTINE* ClientRoutineTable;",
    "RPC_SERVER_INTERFACE* RpcServerInterface;",
    "void* DebugArgumentBuffer;",
    NULL
};

CG_STATUS CG_NETMONSTUB_FILE::EmitDefinitions() {

    if (!m_bDoObject) {
        m_pStream->NewLine();
        m_pStream->Write ("#pragma warning (disable : 4700) /* No warnings from the "\
			"use of uninitialized variables */");
        m_pStream->NewLine();
    }
    m_pStream->WriteBlock (ppszIncludeBlock);

    OpenComment();
    EmitComment ("Structure used to encapsulate all relevant interface information");
    CloseComment();

    m_pStream->NewLine(2);
    m_pStream->Write ("typedef struct _GLOBAL_INTERFACE_DATA {");
    Out_IndentInc( m_pCCB );
    m_pStream->WriteBlock (ppszGlobalInterfaceDataStructure);
    Out_IndentDec( m_pCCB );
    m_pStream->NewLine();
    m_pStream->Write ("} GLOBAL_INTERFACE_DATA;");

    OpenComment();
    EmitComment ("Function used to view unmarshalled argument buffers");
    CloseComment();
    m_pStream->NewLine();
    m_pStream->Write ("static void DebugArgumentBuffer (BYTE* ArgumentBuffer, DWORD BufferSize) "\
        "{ Debug(); }");

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the data used specifically by object interfaces.

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitObjectInterfaceData() {

    CG_ITERATOR I;
    CG_NDR * pCG;

    OpenComment();
    EmitComment ("Data used specifically by object interfaces");
    CloseComment();
    
    // Loop through all interfaces
    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) ) {
        
        if (pCG->GetChild()) {
            
            switch(pCG->GetCGID()) {

            case ID_CG_OBJECT_INTERFACE:
                
                // Emit the object interface RPC_SERVER_INTERFACE structures
                OpenComment();
                EmitComment ("RPC_SERVER_INTERFACE structure for object interface ");
                m_pStream->Write (pCG->GetSymName());
                CloseComment();
                
                EmitRPCServerInterface ((CG_INTERFACE*) pCG);   
                
                break;
                
            default:
                break;
            }
        }
    }

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate an RPC_SERVER_INTERFACE structure for the given interface

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:
 
   Most of the code was taken from misccls.cxx -> CG_INTERFACE::GenServerStub

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitRPCServerInterface (CG_INTERFACE* pCG) {
    
    GUID_STRS TransferSyntaxGuid( TRANSFER_SYNTAX_GUID_STR_1,
                                  TRANSFER_SYNTAX_GUID_STR_2,
                                  TRANSFER_SYNTAX_GUID_STR_3,
                                  TRANSFER_SYNTAX_GUID_STR_4,
                                  TRANSFER_SYNTAX_GUID_STR_5);
    GUID_STRS GuidStrs;
    unsigned short M, m;
    char Buffer[ _MAX_DRIVE + _MAX_DIR + _MAX_FNAME + _MAX_EXT + 1 ];
    long ProtSeqEPCount = 0;
    ITERATOR * pProtSeqIterator;
    
    GuidStrs = pCG->GetGuidStrs();
    
    m_pCCB->SetInterfaceCG (pCG);
    m_pCCB->SetInterfaceName (pCG->GetInterfaceName());
    m_pCCB->GetVersion( &M,&m );
    
    sprintf( Buffer,
            "&%s_%s_DispatchTable",
            pCG->GetInterfaceName(),
            m_pCCB->GenMangledName()
            );

    if ( ( pProtSeqIterator = pCG->GetProtSeqEps() ) != 0 ) 
        {
            ProtSeqEPCount = ITERATOR_GETCOUNT( *pProtSeqIterator );
            Out_EP_Info( m_pCCB, pProtSeqIterator );
        }

    Out_IFInfo( m_pCCB,                         // controller block.
                RPC_S_INT_INFO_TYPE_NAME,       // interface info type name.
                RPC_S_INT_INFO_STRUCT_NAME,     // variable name.
                SIZEOF_RPC_SERVER_INTERFACE,    // string speicifying size.
                GuidStrs,                       // Guid specified in idl
                M,                              // user specified major version
                m,                              // user specified minor version
//                TransferSyntaxGuid,             // ndr identifying guid.
//                NDR_UUID_MAJOR_VERSION,         // ndr's version
//                NDR_UUID_MINOR_VERSION,
                NULL, //Buffer,
                ProtSeqEPCount,                 // if this is 0, then the next
                // 2 fields are ignored by the call.
                PROTSEQ_EP_TYPE_NAME,           // RPC_PROTSEQ_ENDPOINT
                PROTSEQ_EP_VAR_NAME,            // ___RpcProtSeqEndpoint
                m_pCCB->IsNoDefaultEpv(),
                1,
                pCG->HasPipes()
                );

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the debug procedures for all interfaces

 Arguments:
    
 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitDebugProcedures() {

    CG_ITERATOR I;
    CG_NDR * pCG;
    
    CG_PROC * pProc;
    CG_INTERFACE * pInt;

    ID_CG idCg;

    ITERATOR IProc;

    char* pszInterfaceName, * pszLastInterfaceName, * pszOurInterfaceName;

    // Write midl malloc and free if we're writing a classic interface stub
    if (!m_bDoObject) {
        OpenComment();
        EmitComment ("Procedures used by the runtime to allocate and free memory");
        CloseComment();

        m_pStream->NewLine(2);
        m_pStream->Write (
            "static void * __RPC_API midl_user_allocate(size_t len) { return NetmonStubAllocate(len); }");
        m_pStream->NewLine();
        m_pStream->Write (
            "static void __RPC_API midl_user_free(void * ptr) { free(ptr); }");
    }

    OpenComment();
    EmitComment ("Implementation of debug server and client functions for all ");
    if (m_bDoObject) {
        m_pStream->Write ("object");
    } else {
        m_pStream->Write ("classic");
    }
    m_pStream->Write (" interfaces");
    CloseComment();

    // Loop through all interfaces
    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) )
    {
        if (pCG->GetChild()) {
            
            pInt = (CG_INTERFACE*) pCG;
            idCg = pCG->GetCGID();
            
            switch(idCg) {
                
            case ID_CG_INTERFACE:
                
                if (!m_bDoObject) {
                    
                    // Loop through all member functions
                    pInt->GetAllMemberFunctions( IProc );
                    while( ITERATOR_GETNEXT( IProc, pProc ) ) {
                        
                        EmitServerClientDebugProcedure (pProc, FALSE);
                        
                        // Emit the procedure as is (to avoid link errors)
                        m_pStream->NewLine(2);
                        m_pStream->Write ("static");
                        Out_ClientProcedureProlog( m_pCCB, pProc->GetType() );
                        Out_IndentInc( m_pCCB );
                        
                        // Return a variable of the appropriate return value
                        if (((node_proc*) pProc->GetType())->HasReturn()) {
                            m_pStream->Spaces( STANDARD_STUB_TAB );
                            m_pStream->Write( pProc->GetReturnType()->GetSymName() );
                            m_pStream->Write(" RetVal;");
                            m_pStream->NewLine();
                            m_pStream->Write( "return RetVal;" );
                        }
                        
                        // Close the procedure
                        Out_IndentDec( m_pCCB );
                        Out_ProcClosingBrace( m_pCCB );
                    }
                }
                
                break;
                
            case ID_CG_OBJECT_INTERFACE:
                
                if (m_bDoObject) {
                    
                    pszOurInterfaceName = pInt->GetInterfaceName();
                    pszLastInterfaceName = NULL;

                    // Loop through all member functions
                    pInt->GetAllMemberFunctions ( IProc);
                    while( ITERATOR_GETNEXT( IProc, pProc ) ) {
                        
                        // Get the procedure's real interface name
                        pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
                        
                        // Write the function if:
                        // a) We're its real interface.
                        // b) The interface hasn't been used yet and it's not IUnknown
                        // c) The interface name is the same as the last one we used
                        // (so it's OK to use it again)
                        if (strcmp (pszInterfaceName, pszOurInterfaceName) == 0 ||
                            (!m_itOutputInterfaceTable.FindInterface (pszInterfaceName) &&
                             strcmp (pszInterfaceName, "IUnknown") != 0) ||
                            (pszLastInterfaceName != NULL &&
                             strcmp (pszInterfaceName, pszLastInterfaceName) == 0)
                             ) {
                            
                            // Write the server and client debug procedures
                            EmitServerClientDebugProcedure (pProc, TRUE);

                            // Add the interface name to the table of used interfaces
                            m_itOutputInterfaceTable.AddInterface (pszInterfaceName);

                            // Record the last interface name used
                            pszLastInterfaceName = pszInterfaceName;
                        }
                    }
                }
                
                break;
                
            default:
                break;
            }
        }
    }

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate server, client and link stub procedures for a given procedure

 Arguments:
    
      pProc -> The procedure to be processed
      bIsObject ->  TRUE if the procedure belongs to an object interface
                    FALSE otherwise
 Return Value:

    CG_OK   if all is well.
        
 Notes:
 
   This function is long and ugly

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitServerClientDebugProcedure (CG_PROC* pProc, BOOL bIsObject) {

    PNAME pszProcName, pszNewProcName;

    node_proc * pNodeProc = ((node_proc*) pProc->GetType());
    node_param * pRetValParam, * pThisParam, * pParam;
    node_skl * pOldRetType;
    node_base_type * pVoidBaseType;
    type_node_list ITypeNodeList;
    
    short i, siNumArgs;
    PNAME* ppszOldArgName;
    PNAME* ppszNewArgName;
    node_base_attr * pNodeAttribIn, * pNodeAttribOut;

    BOOL bHasReturn = pNodeProc->HasReturn();

    // Get the procedure name
    pszProcName = pProc->GetSymName();
                
    // Rename the procedure to Server__InterfaceName__ProcName
    pszNewProcName = new char [strlen (pProc->GetInterfaceName()) + 
        strlen (pszProcName) + 11];
    sprintf ( pszNewProcName, "Server__%s__%s", pProc->GetInterfaceName(), pszProcName );
    pNodeProc->SetSymName ( pszNewProcName );
                
    // Set void type
    pVoidBaseType = new node_base_type( NODE_VOID, ATTR_BASE );
    pVoidBaseType->SetSymName( "void" );
    pOldRetType = pNodeProc->GetChild();
    pNodeProc->SetChild( pVoidBaseType );
                
    // Rename the arguments to IN__Name, OUT__Name or IN_OUT__name
    i = 0;
    siNumArgs = pNodeProc->GetNumberOfArguments();
        
    ppszOldArgName = new PNAME [siNumArgs];
    ppszNewArgName = new PNAME [siNumArgs];
                
    pNodeProc->GetParameterList (&ITypeNodeList);
    while( ITERATOR_GETNEXT( ITypeNodeList, pParam ) ) {
                
        MIDL_ASSERT (i < siNumArgs);
                    
        ppszOldArgName[i] = pParam->GetSymName();
        ppszNewArgName[i] = new char [strlen (ppszOldArgName[i]) + 10];
                    
        if ((pNodeAttribIn = pParam->GetAttribute (ATTR_IN)) && 
            (pNodeAttribOut = pParam->GetAttribute (ATTR_OUT))) {
            sprintf (ppszNewArgName[i], "IN_OUT__%s", ppszOldArgName[i]);
        } else {
                        
            if (pNodeAttribIn) {
                sprintf (ppszNewArgName[i], "IN__%s", ppszOldArgName[i]);
            } else {
                sprintf (ppszNewArgName[i], "OUT__%s", ppszOldArgName[i]);
            }
        }
        pParam->SetSymName (ppszNewArgName[i]);
        i ++;           
    }
    
    // If proc belongs to an object interface, add the 'this' pointer
    if (bIsObject) {
        pThisParam = new node_param();
        pThisParam->SetAttribute (ATTR_IN);
        pThisParam->SetSymName ("this");
        pThisParam->SetBasicType( (node_skl *) new node_def( "void*" ));
        pThisParam->SetEdgeType( EDGE_USE );
                    
        pNodeProc->AddFirstMember (pThisParam);
    }

    // Emit the server procedure
    m_pStream->NewLine(2);
    m_pStream->Write ("static");
    Out_ClientProcedureProlog( m_pCCB, pNodeProc );
    Out_IndentInc( m_pCCB );
    m_pStream->Spaces( STANDARD_STUB_TAB );
    m_pStream->Write( "Debug();" );
    Out_IndentDec( m_pCCB );
    Out_ProcClosingBrace( m_pCCB );
    
    // Rename the procedure to Client__InterfaceName__ProcName
    sprintf( pszNewProcName, "Client__%s__%s", pProc->GetInterfaceName(), pszProcName );
    
    // Add a RetVal parameter to the param list if the function isn't void
    if (bHasReturn) {
            
        pRetValParam = new node_param();
        pRetValParam->SetAttribute (ATTR_IN);
        pRetValParam->SetSymName ("RetVal");
        pRetValParam->SetBasicType( (node_skl *) new node_def( pProc->GetReturnType()->GetSymName() ));
        pRetValParam->SetEdgeType( EDGE_USE );
                    
        pNodeProc->AddLastMember (pRetValParam);
    }
                
    // Emit the client procedure
    m_pStream->NewLine(2);
    m_pStream->Write ("static");
    Out_ClientProcedureProlog( m_pCCB, pNodeProc );
    Out_IndentInc( m_pCCB );
    m_pStream->Spaces( STANDARD_STUB_TAB );
    m_pStream->Write( "Debug();" );
    Out_IndentDec( m_pCCB );
    Out_ProcClosingBrace( m_pCCB );
    
    // Delete the this parameter we created
    if (bIsObject) {
        pNodeProc->RemoveFirstMember();
        delete pThisParam;
    }

    // Delete the RetVal from the param List
    if (bHasReturn) {
        pNodeProc->RemoveLastMember();
        delete pRetValParam;
    }
                
    // Restore the procedure's name
    pNodeProc->SetSymName( pszProcName );
    
    // Restore the procedure's parameter names
    if (siNumArgs > 0) {
        i = 0;
        pNodeProc->GetParameterList (&ITypeNodeList);
        while( ITERATOR_GETNEXT( ITypeNodeList, pParam ) ) {
            pParam->SetSymName (ppszOldArgName[i]);
            delete [] ppszNewArgName[i];
            i ++;
        }
        delete [] ppszNewArgName;
        delete [] ppszOldArgName;
    }

    // Restore the node's return type
    pNodeProc->SetChild( pOldRetType );
    
    // Delete the fake void type node we created
    delete pVoidBaseType;
                
    // Delete the new name we created
    delete [] pszNewProcName;

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate data tables for each interface

 Arguments:

 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitDataTables() {

    // Write procedure name tables for each interface
    EmitProcNameTables();

    // Write the server and client lookup tables for each interface
    EmitServerClientTables();

    // Write the global_interface_data structures and exports for each interface
    EmitGlobalInterfaceData();

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate tables with each interface's procedure names and the number of 
    procedures in each interface.

 Arguments:

 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitProcNameTables() {

    CG_ITERATOR I;
    CG_NDR * pCG;
    CG_INTERFACE* pInt;

    ITERATOR IProc;
    CG_PROC * pProc;

    long lNumProcs;
    BOOL bBeenHereBefore;

    char* pszInterfaceName;
    char pszTemp [1024];

    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) ) {
        
        if (pCG->GetChild()) {
            
            pInt = (CG_INTERFACE*) pCG;
            
            if (m_bDoObject && pCG->GetCGID() == ID_CG_OBJECT_INTERFACE ||
                !m_bDoObject && pCG->GetCGID() == ID_CG_INTERFACE) {
                
                lNumProcs = 0;
                bBeenHereBefore = FALSE;
                
                pszInterfaceName = pInt->GetInterfaceName();
                
                OpenComment();
                EmitComment ("Procedure tables for interface ");
                m_pStream->Write (pszInterfaceName);
                CloseComment();
                
                m_pStream->NewLine();
                sprintf (pszTemp, "static char* %s__ProcedureNames[] = {", pszInterfaceName);
                m_pStream->Write (pszTemp);
                
                Out_IndentInc( m_pCCB );
                
                pInt->GetAllMemberFunctions ( IProc);
                while( ITERATOR_GETNEXT( IProc, pProc ) ) {
                    
                    pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
                    if (strcmp (pszInterfaceName, "IUnknown") != 0) {
                        
                        // Write a comma if we're not the first function in the list
                        if (bBeenHereBefore) {
                            m_pStream->Write (",");
                        } else {
                            bBeenHereBefore = TRUE;
                        }

                        // Write procedure name enclosed in quotes
                        m_pStream->NewLine();
                        m_pStream->Write ("\"");
                        m_pStream->Write (pProc->GetSymName());
                        m_pStream->Write ("\"");

                        // Increment procedure count
                        lNumProcs ++;
                    }
                }
                
                Out_IndentDec( m_pCCB );
                m_pStream->NewLine();
                m_pStream->Write ("};");

                // Set number of procedures in interface table
                m_itInterfaceTable.SetNumProcedures (pszInterfaceName, lNumProcs);
            }
        }   // if
    }   // while

    return CG_OK;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the server and client lookup tables for each interface

 Arguments:

 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitServerClientTables() {

    CG_ITERATOR I;
    CG_NDR * pCG;
    CG_INTERFACE* pInt;

    ITERATOR IProc;
    CG_PROC * pProc;

    char* pszInterfaceName;

    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) ) {
    
        if (pCG->GetChild()) {
            
            if (m_bDoObject && pCG->GetCGID() == ID_CG_OBJECT_INTERFACE ||
                !m_bDoObject && pCG->GetCGID() == ID_CG_INTERFACE) {
                
                pInt = (CG_INTERFACE*) pCG;
                pszInterfaceName = pInt->GetInterfaceName();
                
                // Write the server table
                OpenComment();
                EmitComment ("Debug server procedures for interface ");
                m_pStream->Write (pszInterfaceName);
                CloseComment();
                m_pStream->NewLine();
                
                m_pStream->Write ("static SERVER_ROUTINE ");
                m_pStream->Write (pszInterfaceName);
                m_pStream->Write ("__ServerRoutineTable[] = {");
                Out_IndentInc( m_pCCB );
                
                // If we're processing an object interface, 
                // we have to make room for the 3 IUnknown procedures
                if (m_bDoObject) {
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                }
                
                pInt->GetAllMemberFunctions ( IProc);
                while( ITERATOR_GETNEXT( IProc, pProc ) ) {
                    
                    pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
                    if (strcmp (pszInterfaceName, "IUnknown") != 0) {
                        m_pStream->NewLine();
                        m_pStream->Write ("(SERVER_ROUTINE)Server__");
                        m_pStream->Write (pProc->GetInterfaceNode()->GetSymName());
                        m_pStream->Write ("__");
                        m_pStream->Write (pProc->GetSymName());
                        m_pStream->Write (",");
                    }
                }
                Out_IndentDec( m_pCCB );
                m_pStream->NewLine();
                m_pStream->Write ("};");
                
                // Write client table
                OpenComment();
                EmitComment ("Debug client procedures for interface ");
                m_pStream->Write (pszInterfaceName);
                CloseComment();
                m_pStream->NewLine();
                
                m_pStream->Write ("static SERVER_ROUTINE ");
                m_pStream->Write (pszInterfaceName);
                m_pStream->Write ("__ClientRoutineTable[] = {");
                Out_IndentInc( m_pCCB );
                
                // If we're processing an object interface, 
                // we have to make room for the 3 IUnknown procedures
                if (m_bDoObject) {
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                    m_pStream->NewLine();
                    m_pStream->Write ("NULL,");
                }
                
                pInt->GetAllMemberFunctions ( IProc);
                while( ITERATOR_GETNEXT( IProc, pProc ) ) {
                    
                    pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
                    if (strcmp (pszInterfaceName, "IUnknown") != 0) {
                        m_pStream->NewLine();
                        m_pStream->Write ("(SERVER_ROUTINE)Client__");
                        m_pStream->Write (pProc->GetInterfaceNode()->GetSymName());
                        m_pStream->Write ("__");
                        m_pStream->Write (pProc->GetSymName());
                        m_pStream->Write (",");
                    }
                }
                Out_IndentDec( m_pCCB );
                m_pStream->NewLine();
                m_pStream->Write ("};");
            }
        }
    }

    return CG_OK;
}



/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Generate the global interface data structures for each interface, as well
    as the export functions that return them

 Arguments:

 Return Value:

    CG_OK   if all is well.
        
 Notes:

----------------------------------------------------------------------------*/
CG_STATUS CG_NETMONSTUB_FILE::EmitGlobalInterfaceData() {

    CG_ITERATOR I;
    CG_NDR * pCG;
    ID_CG idCG;
    
    char pszTemp [100], * pszInterfaceName, * pszFixedUuid;
    char * p1, * p2, * p3, * p4, * p5;
    long lNumProcs;
    size_t lLength;

    GetMembers( I );
    while( ITERATOR_GETNEXT( I, pCG ) ) {

        idCG = pCG->GetCGID();

        if (((idCG == ID_CG_OBJECT_INTERFACE && m_bDoObject) || 
            (idCG == ID_CG_INTERFACE && !m_bDoObject)) &&
            pCG->GetChild()) {

            // Get the underscored uuid
            ((CG_INTERFACE*) pCG)->GetGuidStrs().GetStrs (p1, p2, p3, p4, p5);
            lLength = (long) strlen (p1) + strlen (p2) + strlen (p3) + strlen (p4) + strlen (p5);
            pszFixedUuid = new char [lLength + 5];
            sprintf (pszFixedUuid, "%s_%s_%s_%s_%s", p1, p2, p3, p4, p5);
            pszFixedUuid = _strlwr (pszFixedUuid);

            // Get the interface name
            pszInterfaceName = pCG->GetSymName();

            // Write a comment
            OpenComment();
            EmitComment ("GLOBAL_INTERFACE_DATA structure for interface ");
            m_pStream->Write (pszInterfaceName);
            CloseComment();

            // Header
            m_pStream->NewLine();
            m_pStream->Write ("static GLOBAL_INTERFACE_DATA GlobalInterfaceData__");
            m_pStream->Write (pszFixedUuid);
            m_pStream->Write (" = {");
            Out_IndentInc( m_pCCB );

			// Version
			m_pStream->NewLine();
			m_pStream->Write (NETMON_STUB_VERSION);
			m_pStream->Write (",");

            // Name
            m_pStream->NewLine();
            m_pStream->Write ("\"");
            m_pStream->Write (pszInterfaceName);
            m_pStream->Write ("\",");

            // Proc format string size
            m_pStream->NewLine();
            m_pStream->Write ("PROC_FORMAT_STRING_SIZE,");

            // NumProcs
            m_pStream->NewLine();
            if (m_itInterfaceTable.GetNumProcedures (pszInterfaceName, &lNumProcs)) {
                m_pStream->Write (_itoa (lNumProcs, pszTemp, 10));
            } else {
                MIDL_ASSERT (FALSE);
            }
            m_pStream->Write (",");

            // Proc name table
            m_pStream->NewLine();
            m_pStream->Write (pszInterfaceName);
            m_pStream->Write ("__ProcedureNames,");

            // Server routine table
            m_pStream->NewLine();
            m_pStream->Write (pszInterfaceName);
            m_pStream->Write ("__ServerRoutineTable,");

            // Client routine table
            m_pStream->NewLine();
            m_pStream->Write (pszInterfaceName);
            m_pStream->Write ("__ClientRoutineTable,");
            
            // Rpc server interface pointer
            m_pStream->NewLine();
            m_pStream->Write ("(RPC_SERVER_INTERFACE*) &");
            m_pStream->Write (pszInterfaceName);
            m_pStream->Write ("___RpcServerInterface,");

            // DebugArgumentBuffer
            m_pStream->NewLine();
            m_pStream->Write ("(void*) DebugArgumentBuffer");

            Out_IndentDec( m_pCCB );
            m_pStream->NewLine();
            m_pStream->Write ("};");

            // Export function
            OpenComment();
            EmitComment ("Export function for interface ");
            m_pStream->Write (pszInterfaceName);
            CloseComment();

            m_pStream->NewLine();
            m_pStream->Write ("DLL_EXPORT_PROC (");
            m_pStream->Write (pszFixedUuid);
            m_pStream->Write (")");

            delete [] pszFixedUuid;
        }
    }

    return CG_OK;
}

/****************************************************
* InterfaceTable implementations
****************************************************/
#define NUM_BUCKETS 100
NetmonStubFileInterfaceTable::NetmonStubFileInterfaceTable() {
    m_pTable = new NetmonStubFileInterfaceList [NUM_BUCKETS];
}

NetmonStubFileInterfaceTable::~NetmonStubFileInterfaceTable() {
    delete [] m_pTable;
}

void NetmonStubFileInterfaceTable::AddInterface (char* pszInterface) {
    m_pTable[GetHashValue (pszInterface)].AddInterface (pszInterface);  
}

// Return true if the interface name was found
BOOL NetmonStubFileInterfaceTable::FindInterface (char* pszInterface) {
    return m_pTable[GetHashValue (pszInterface)].FindInterface (pszInterface);
}

// Set the number of procedures in the interface
BOOL NetmonStubFileInterfaceTable::SetNumProcedures (char* pszInterface, long lNumProcs) {
    return m_pTable[GetHashValue (pszInterface)].SetNumProcedures (pszInterface, lNumProcs);
}

// Get the number of procedures in the interface
BOOL NetmonStubFileInterfaceTable::GetNumProcedures (char* pszInterface, long* plNumProcs) {
    return m_pTable[GetHashValue (pszInterface)].GetNumProcedures (pszInterface, plNumProcs);
}

// The hash value is just the sum of the characters in the interface name
// mod the number of buckets in the table
long NetmonStubFileInterfaceTable::GetHashValue (char* pszInterface) {
    
    long i, lSum = 0, lLen = (long) strlen (pszInterface);
    for (i = 0; i < lLen; i ++) {
        lSum += (long) pszInterface[i];
    }
    return lSum % NUM_BUCKETS;
}


/* InterfaceNode */
NetmonStubFileInterfaceNode::NetmonStubFileInterfaceNode (char* pszInterface) {
    m_pszInterface = new char [strlen (pszInterface) + 1];
    strcpy (m_pszInterface, pszInterface);
    m_pNext = NULL;
    m_lNumProcs = 0;
}

NetmonStubFileInterfaceNode::~NetmonStubFileInterfaceNode() {
    delete [] m_pszInterface;
}

void NetmonStubFileInterfaceNode::SetNext (NetmonStubFileInterfaceNode* pNext) {
    m_pNext = pNext;
}

NetmonStubFileInterfaceNode* NetmonStubFileInterfaceNode::GetNext() {
    return m_pNext;
}

char* NetmonStubFileInterfaceNode::GetInterface() {
    return m_pszInterface;
}

void NetmonStubFileInterfaceNode::SetNumProcedures (long lNumProcs) {
    m_lNumProcs = lNumProcs;
}

long NetmonStubFileInterfaceNode::GetNumProcedures() {
    return m_lNumProcs;
}

/* InterfaceList */
NetmonStubFileInterfaceList::NetmonStubFileInterfaceList() {
    m_pHead = NULL;
    m_pTail = NULL;
}

NetmonStubFileInterfaceList::~NetmonStubFileInterfaceList() {
    NetmonStubFileInterfaceNode* pNode = m_pHead, *pDeleteNode;
    while (pNode != NULL) {
        pDeleteNode = pNode;
        pNode = pNode->GetNext();
        delete pDeleteNode;
    }
}

void NetmonStubFileInterfaceList::AddInterface (char* pszInterface) {
    
    NetmonStubFileInterfaceNode* pNode = new NetmonStubFileInterfaceNode (pszInterface);
        
    if (m_pHead == NULL) {
        m_pHead = m_pTail = pNode;
    } else {
        m_pTail->SetNext (pNode);
        m_pTail = pNode;
    }
}

BOOL NetmonStubFileInterfaceList::FindInterface (char* pszInterface) {

    NetmonStubFileInterfaceNode* pNode = m_pHead;
    while (pNode != NULL) {
        if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
            return TRUE;
        }
        pNode = pNode->GetNext();
    }

    return FALSE;
}

BOOL NetmonStubFileInterfaceList::SetNumProcedures (char* pszInterface, long lNumProcs) {

    NetmonStubFileInterfaceNode* pNode = m_pHead;
    while (pNode != NULL) {
        if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
            pNode->SetNumProcedures (lNumProcs);
            return TRUE;
        }
        pNode = pNode->GetNext();
    }

    return FALSE;
}

BOOL NetmonStubFileInterfaceList::GetNumProcedures (char* pszInterface, long* plNumProcs) {
    
    NetmonStubFileInterfaceNode* pNode = m_pHead;
    while (pNode != NULL) {
        if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
            *plNumProcs = pNode->GetNumProcedures();
            return TRUE;
        }
        pNode = pNode->GetNext();
    }

    return FALSE;
}
