/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_soltools.hxx"

#include <string.h>
#include <direct.h>
#include <stdio.h>
#include <stdlib.h>

#include "ldump.hxx"
#include "hashtbl.hxx"

#define MAXSYM     65536
#define MAXBASE    98304
#define MAX_MAN		4096

int	bFilter = 0;
int bLdump3 = 0;
int bUseDirectives = 0;
int bVerbose = 0;
int bExportByName = 0;

class ExportSet : public HashTable
{
public:
    ExportSet
    (
        unsigned long lSize,
        double	dMaxLoadFactor = HashTable::m_defMaxLoadFactor,
        double	dGrowFactor = HashTable::m_defDefGrowFactor
    )
        : HashTable(lSize,false,dMaxLoadFactor,dGrowFactor) {}

    virtual ~ExportSet() {}

    LibExport *  Find (char * const& Key) const
    { return (LibExport *) HashTable::Find((char *) Key); }

    bool Insert (char * const& Key, LibExport * Object)
    { return HashTable::Insert((char *) Key, (void*) Object); }

    LibExport *  Delete (char * const&Key)
    { return (LibExport *) HashTable::Delete ((char *) Key); }
};

LibDump::LibDump( char *cFileName, int bExportByName )
				: cBName( NULL ),
				cAPrefix( NULL ),
				cLibName( NULL ),
				cFilterName( NULL ),
				cModName( NULL )
{
	fprintf( stderr, "LIB-NT File Dumper v4.00 (C) 2000 Sun Microsystems, Inc.\n\n" );
	fprintf( stderr, "%s ", cFileName );

    bExportName = bExportByName;

	unsigned long nSlots =  0xfffff;
	pBaseTab = new ExportSet( nSlots );
	pIndexTab = new ExportSet( nSlots );
	pFilterLines = new char * [MAXFILT];
	CheckLibrary(cFileName);
	bBase = 0;
	bAll = false;
	nDefStart = 0;
	nBaseLines = 0;
	nFilterLines = 0;
	bDef = true;
	cAPrefix = new char[ 1 ];
	cAPrefix[ 0 ] = 0;
    if (!bExportName)
	    CheckDataBase();
}

bool LibDump::Dump()
{
	FILE *pList;
    char  aBuf[MAX_MAN];
    int   nLen;
    char  aName[MAX_MAN];

    pList = fopen( cLibName, "rb");
	if (!pList)
		DumpError(10);

    // forget about offset when working on linker directives
    if ( !bUseDirectives )
    {
        // calculating offset for name section
    	unsigned char TmpBuffer[4];
	    fread( TmpBuffer, 1, 4, pList);
    	// anzahl bigendian mal laenge + ueberspringen der naechsten laengenangabe
    	unsigned long nOffSet = (unsigned long) ( TmpBuffer[2] * 256 + TmpBuffer[3] ) * 4 + 4;
    	fseek( pList, (long) nOffSet, 0);
    }

	char aTmpBuf[4096];
	// reading file containing symbols
    while( !feof( pList ) )
    {
   		int i = 0;
        if ( !bUseDirectives )
        {
	    	// symbol komplett einlesen
            for (;;)
            {
                int c = fgetc( pList );
                if ( c == '\0' )
                {
                    break;
                }
                if ( ((c >= 33) && (c <= 126)) && ( c!=40 && c!=41) )
                	aBuf[i] = static_cast< char >(c);
                else
                {
                    aBuf[0] = '\0';
                    break;
                }
            	i++;
            }
    		// Namen found
            aBuf[i] = '\0';
        }
        else
        {
            fgets( aTmpBuf, 4096, pList );
            char * pEnd = 0;
            char *pFound = 0;
            aBuf[0] = '\0';
            pFound = strchr( aTmpBuf, 'E' );
            while ( pFound )
            {
                if ( strncmp( "EXPORT:", pFound, 7) == 0 )
                {
                    pFound += 7;
                    pEnd = strchr( pFound, ',');
                    if ( pEnd )
                        *pEnd = '\0';
                    strncpy( aBuf, pFound, strlen( pFound));
                    aBuf[ strlen( pFound) ] = '\0';
//                    fprintf( stderr, "\n--- %s\n", aBuf);
                    break;
                }
                else
                {
                    pFound++;
                    pFound = strchr( pFound, 'E' );
                }
            }
        }

	    if ((aBuf[0] =='?') || !strncmp(aBuf, "__CT",4))
		{
			nLen = (int) strlen(aBuf);
            memset( aName, 0, sizeof( aName ) );
            int nName = 0;
            for( i = 0; i < nLen; i++ )
            {
                if ( (aBuf[i] != '\n') && (aBuf[i] != '\r') )
                {
                    aName[nName] = aBuf[i];
                    nName++;
                }
            }
			// und raus damit
            PrintSym( aName, bExportByName );
		}
		else if ( bAll == true )
		{
			int nPreLen = (int) strlen( cAPrefix );

            nLen = (int) strlen(aBuf);
            memset( aName, 0, sizeof( aName ) );
            int nName = 0;

            for( i = 0; i < nLen; i++ )
            {
              if ( (aBuf[i] != '\n') && (aBuf[i] != '\r') )
              {
                  aName[nName] = aBuf[i];
                  nName++;
			  }
			}
			//fprintf( stderr, "Gefundenen Prefix : %s %d \n", aTmpBuf, nPreLen );
			// den ersten _ raus
            nLen = (int) strlen(aName);
            if (aName[0] == '_')
				strcpy( aBuf , &aName[1] );
			strncpy ( aTmpBuf, aBuf, (size_t) nPreLen );
			aTmpBuf[nPreLen] = '\0';
			if ( !strcmp( aTmpBuf, cAPrefix ))
			{
				if ( bLdump3 ) {
					int nChar = '@';
					char *pNeu = strchr( aBuf, nChar );
					int nPos = pNeu - aBuf + 1;
					if ( nPos > 0 )
					{
						char aOldBuf[MAX_MAN];
						strcpy( aOldBuf, aBuf );
						char pChar[MAX_MAN];
						strncpy( pChar, aBuf, (size_t) (nPos -1) );
						pChar[nPos-1] = '\0';
						strcpy( aBuf, pChar );
						strcat( aBuf, "=" );
						strcat( aBuf, aOldBuf );
						strcpy( pChar, "" );
					}
				}
				// und raus damit
            	PrintSym( aBuf, true );
			}
		}
    }
	fclose(pList);
	return true;
}

bool LibDump::ReadFilter( char * cFilterName )
{
	FILE* pfFilter = 0;
    char  aBuf[MAX_MAN];
    char* pStr;
    int   nLen;

    pfFilter = fopen( cFilterName, "r" );

    if ( !pfFilter )
    {
        ::bFilter = 0;
        DumpError( 500 );
    }

    while( fgets( aBuf, MAX_MAN, pfFilter ) != 0 )
    {
        nLen = (int) strlen(aBuf);
        pStr = new char[(unsigned int) nLen];
        if ( !pStr )
            DumpError( 98 );
        memcpy( pStr, aBuf, (unsigned int) nLen );
        if ( *(pStr+nLen-1) == '\n' )
            *(pStr+nLen-1) = '\0';
		pFilterLines[nFilterLines] = pStr;
		nFilterLines++;
		if ( nFilterLines >= MAXFILT )
				DumpError( 510 );
    }

	fclose( pfFilter );
	return true;
}

bool LibDump::PrintSym(char *pName, bool bName )
{
	LibExport *pData;


    // Filter auswerten
    if ( Filter( pName ) )
    {
        if ( strlen( pName ) > 3 )
        {
            if ( bDef )
		    {
		        if (!bBase)
                    if (bExportName) {
						fprintf( stdout, "\t%s\n", pName );
                    } else {
						fprintf( stdout, "\t%s\t\t@%lu\n", pName, nDefStart );
                    }
				else
				{
					 pData = pBaseTab->Find( pName );
					 if ( pData )
					 {
						pData->bExport = true;
						if ( bName )
							pData->bByName = true;
						else
							pData->bByName = false;
						if ( bVerbose )
							fprintf(stderr,".");
					 }
					 else
					 {
					 	// neuen Export eintragen
					 	pData = new LibExport;
						pData->cExportName = new char[ strlen( pName ) + 1 ];
						strcpy( pData->cExportName, pName );
						pData->nOrdinal = nBaseLines++;
						pData->bExport = true;
						if ( bName )
							pData->bByName = true;
						else
							pData->bByName = false;
						pBaseTab->Insert( pData->cExportName, pData );
						char *cBuffer = new char[ 30 ];
						sprintf( cBuffer, "%lu", pData->nOrdinal );
						pIndexTab->Insert( cBuffer, pData );
						delete [] cBuffer;
						if ( bVerbose )
							fprintf(stderr,"n");
					 }
				}
		    }
            else
                printf( "%s\n", pName );
            nDefStart++;
        }
    }
	return true;
}

bool LibDump::IsFromAnonymousNamespace (char *pExportName) {
    char* pattern1 = "@?A0x";

    if (strstr(pExportName, pattern1)) {
        return true; 
    };
    return false; 
};

bool LibDump::Filter(char *pExportName)
{
	unsigned long i;
	char pTest[256];
    
    // filter out symbols from anonymous namespaces
    if (IsFromAnonymousNamespace (pExportName))
        return false;
    
    // Kein Filter gesetzt
    if ( ::bFilter == 0 )
        return true;

	for ( i=0; i<nFilterLines; i++ )
	{
		//Zum vergleichen muá das Plus abgeschnitteb werden
		if(pFilterLines[i][0] != '+')
		{
			if ( strstr( pExportName, pFilterLines[i]))
				return false;
		}
		else
		{
			strcpy(pTest,&pFilterLines[i][1]);
			if ( strstr( pExportName, pTest))
				return true;
		}
	}
    return true;
}

bool LibDump::SetFilter(char * cFilterName)
{
	ReadFilter( cFilterName );
	return true;
}

bool LibDump::CheckLibrary(char * cName)
{
	delete [] cLibName;
	cLibName = new char[ strlen( cName ) + 1 ];
	strcpy( cLibName, cName );
	return true;
}

bool LibDump::ReadDataBase()
{
	FILE* pfBase = 0;
    char  aBuf[MAX_MAN];
    char* pStr;
    char  cBuffer[ 30 ];
    int   nLen;
	LibExport *pData;

    pfBase = fopen( cBName, "r" );

    if ( !pfBase )
    {
        bBase = 0;
        DumpError( 600 );
    }

	bool bRet = true;
	while( fgets( aBuf, MAX_MAN, pfBase ) != 0 )
    {
        nLen = (int) strlen(aBuf);
        pStr = new char[(unsigned int) nLen];
        if ( !pStr )
            DumpError( 98 );
        memcpy( pStr, aBuf, (size_t) nLen );
        if ( *(pStr+nLen-1) == '\n' )
            *(pStr+nLen-1) = '\0';
		pData = new LibExport;
		pData->cExportName = pStr;
		pData->nOrdinal = nBaseLines;
		pData->bExport=false;

		if (pBaseTab->Insert(pData->cExportName, pData ) == NULL)
			bRet = false;
        ltoa( (long) pData->nOrdinal, cBuffer, 10 );
		if (pIndexTab->Insert( cBuffer, pData ) == NULL)
			bRet = false;
        nBaseLines++;
        if ( nBaseLines >= MAXBASE )
            DumpError( 610 );
    }
    fclose( pfBase );
	return bRet;
}

class ExportSetIter : public HashTableIterator
{
public:
    ExportSetIter(HashTable const& aTable)
		: HashTableIterator(aTable)	{}

    LibExport * GetFirst()
    { return (LibExport *)HashTableIterator::GetFirst(); }
    LibExport * GetNext()
    { return (LibExport *)HashTableIterator::GetNext();  }
    LibExport * GetLast()
    { return (LibExport *)HashTableIterator::GetLast();  }
    LibExport * GetPrev()
    { return (LibExport *)HashTableIterator::GetPrev();  }

private:
    void operator =(ExportSetIter &); // not defined
};

bool LibDump::PrintDataBase()
{
    if (bExportName) 
        return true;
	FILE *pFp;
	pFp = fopen (cBName,"w+");
	if (!pFp)
		fprintf( stderr, "Error opening DataBase File\n" );

	LibExport *pData;
	for ( unsigned long i=0; i < nBaseLines+10; i++ )
	{
		char * cBuffer = new char[ 30 ];
		sprintf( cBuffer, "%lu", i );
		pData = pIndexTab->Find( cBuffer );
		delete [] cBuffer;
		if ( pData )
			fprintf(pFp,"%s\n",pData->cExportName);
	}
	fclose(pFp);
	return true;
}

bool LibDump::PrintDefFile()
{
#ifdef FAST
	ExportSetIter aIterator( *pBaseTab );
	for ( LibExport *pData = aIterator.GetFirst(); pData != NULL;
										pData = aIterator.GetNext() )
	{
		if ( pData->bExport )
		{
			if ( pData->bByName )
			{
				fprintf(stdout,"\t%s\n",
					pData->sExportName.GetBuffer());
			}
			else
			{
				fprintf(stdout,"\t%s\t\t@%d NONAME\n",
					pData->sExportName.GetBuffer(), pData->nOrdinal+nBegin);
			}
		}
	}
#else
	// sortiert nach Ordinals;
	LibExport *pData;
	for ( unsigned long i=0; i<nBaseLines+1; i++)
	{
		char * cBuffer = new char[ 30 ];
		sprintf( cBuffer, "%lu", i );
		pData = pIndexTab->Find( cBuffer );
		delete [] cBuffer;
		if ( pData )
			if ( pData->bExport )
			{
				if ( pData->bByName )
				{
					if ( strlen( pData->cExportName ))
						fprintf(stdout,"\t%s\n",
							pData->cExportName);
				}
				else
				{
					if ( strlen( pData->cExportName ))
						fprintf(stdout,"\t%s\t\t@%d NONAME\n",
							pData->cExportName, pData->nOrdinal+nBegin);
				}
			}
	}
#endif
	return true;
}

bool LibDump::CheckDataBase()
{
    // existiert eine Datenbasis ?
    if (!bBase)
    {
		cBName = new char[ 2048 ];
		char *pTmp = "defs\\";

    	FILE *fp;
#ifdef OS2
		_mkdir ("defs", 0777);
#else
		_mkdir ("defs");
#endif
		strcpy(cBName,pTmp);
#ifdef OS2
		strcat(cBName,"gcc");
#else
		strcat(cBName,getenv ("COMP_ENV"));
#endif

		fp = fopen (cBName,"r");
		if (fp)
		{
			bBase = true;
		}
		else
		{
			fp = fopen (cBName,"w+");
			bBase = true;
		}
		fclose (fp);
    }
    // lese Datenbasis !
    if (bBase)
	{
    	ReadDataBase();
	}
	return true;
}

LibDump::~LibDump()
{
	delete [] cBName;
	delete [] cAPrefix;
//	delete [] cLibName;
	delete [] cFilterName;
	delete [] cModName;
}

void LibDump::SetCExport( char* pName )
{
	delete [] cAPrefix;
	cAPrefix = new char[ strlen( pName ) + 1 ];
	strcpy( cAPrefix, pName );bAll = true;
}

//******************************************************************
//* Error() - Gibt Fehlermeldumg aus
//******************************************************************

void LibDump::DumpError( unsigned long n )
{
    char *p;

    switch (n)
    {
        case 1:  p = "Input error in library file"; break;
        case 2:  p = "Position error in library file (no THEADR set)"; break;
        case 3:  p = "Overflow of symbol table"; break;
#ifdef WNT
        case 10: p = "EXP file not found"; break;
        case 11: p = "No valid EXP file"; break;
#else
        case 10: p = "Library file not found"; break;
        case 11: p = "No valid library file"; break;
#endif
        case 98: p = "Out of memory"; break;
        case 99: p = "LDUMP [-LD3] [-D] [-N] [-A] [-E nn] [-F name] Filename[.LIB]\n"
					 "-LD3   : Supports feature set of ldump3 (default: ldump/ldump2)\n"
                     "-A     : all symbols (default: only C++)\n"
                     "-E nn  : gerenration of export table beginning with number nn\n"
                     "-F name: Filter file\n"
                     "-D     : file contains \"dumpbin\" directives\n"
                     "-N     : export by name\n"
                     "-V     : be verbose\n"; break;
        case 500: p = "Unable to open filter file\n"; break;
        case 510: p = "Overflow of filter table\n"; break;
        case 600: p = "Unable to open base database file\n"; break;
        case 610: p = "Overflow in base database table\n"; break;
        default: p = "Unspecified error";
    }
	fprintf( stdout, "%s\n", p );
    exit (1);
}

/*********************************************************************
		Test Funktionen
*********************************************************************/


void usage()
{
	LibDump::DumpError(99);
}

#define STATE_NON		0x0000
#define STATE_BEGIN		0x0001
#define STATE_FILTER	0x0002
#define STATE_CEXPORT	0x0003

int
#ifdef WNT
__cdecl
#endif
main( int argc, char **argv )
{
	char *pLibName = NULL, *pFilterName = NULL, *pCExport= NULL;
	unsigned short nBegin=1;

	unsigned short nState = STATE_NON;

	if ( argc == 1 ) {
		usage();
	}

	for ( int i = 1; i < argc; i++ ) {
		if (( !strcmp( argv[ i ], "-H" )) ||
			( !strcmp( argv[ i ], "-h" )) ||
			( !strcmp( argv[ i ], "-?" )))
		{
			usage();
		}
		else if (( !strcmp( argv[ i ], "-LD3" )) ||
			( !strcmp( argv[ i ], "-Ld3" )) ||
			( !strcmp( argv[ i ], "-ld3" )) ||
			( !strcmp( argv[ i ], "-lD3" )))
		{
			if ( nState != STATE_NON ) {
				usage();
			}
			bLdump3 = 1;
		}
		else if (( !strcmp( argv[ i ], "-E" )) || ( !strcmp( argv[ i ], "-e" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
			nState = STATE_BEGIN;
		}
		else if (( !strcmp( argv[ i ], "-F" )) || ( !strcmp( argv[ i ], "-f" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
			nState = STATE_FILTER;
		}
		else if (( !strcmp( argv[ i ], "-A" )) || ( !strcmp( argv[ i ], "-a" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
			nState = STATE_CEXPORT;
			pCExport = new char[ 1 ];
			pCExport[ 0 ] = 0;
		}
		else if (( !strcmp( argv[ i ], "-D" )) || ( !strcmp( argv[ i ], "-d" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
            bUseDirectives = 1;
		}
		else if (( !strcmp( argv[ i ], "-N" )) || ( !strcmp( argv[ i ], "-n" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
            bExportByName = 1;
		}
		else if (( !strcmp( argv[ i ], "-V" )) || ( !strcmp( argv[ i ], "-v" ))) {
			if ( nState != STATE_NON ) {
				usage();
			}
            bVerbose = 1;
		}
		else {
			switch ( nState ) {
				case STATE_BEGIN:
					nBegin = static_cast< unsigned short >(atoi( argv[ i ] ));
					nState = STATE_NON;
				break;
				case STATE_FILTER:
					pFilterName = new char[ strlen( argv[ i ] ) + 1 ];
					strcpy( pFilterName, argv[ i ] );
					bFilter = 1;
					nState = STATE_NON;
				break;
				case STATE_CEXPORT:
					delete [] pCExport;
					pCExport = new char[ strlen( argv[ i ] ) + 1 ];
					strcpy( pCExport, argv[ i ] );
					nState = STATE_NON;
				break;
				default:
					pLibName = new char[ strlen( argv[ i ] ) + 1 ];
					strcpy( pLibName, argv[ i ] );
				break;
			}
		}
	}

	if ( !pLibName ) {
		usage();
	}

	LibDump *pDump = new LibDump( pLibName, bExportByName );
	pDump->SetBeginExport(nBegin);
	if ( bFilter != 0 )
		pDump->SetFilter( pFilterName );
	if ( pCExport )
		pDump->SetCExport( pCExport );
	else {
		char *pEmpty = "";
		pDump->SetCExport( pEmpty );
	}
	pDump->Dump();
	pDump->PrintDefFile();
	pDump->PrintDataBase();
	delete pDump;
	return 0;
}
