/*
** AutoWrap.C
**
** The AutoWrap tool.
** 
** Copyright(C) 1994 Microsoft Corporation
** All rights reserved.
**
**  091694 -- JHSimon @ IBM modified for PPC
**
**
*/
#include <windows.h>
#include <stdio.h>
#include "autowrap.h"
#include "scan.h"

/*
** prototypes
*/
BOOL  GenerateDirectoryTree(void) ;     // Dir Tree (OBJ, i386,MIPS,ALPHA,PPC)
BOOL  GenerateAPIFiles(void) ;          // WAPI.H, ZAPI.ASM(.S), Z*.LST, z*.def
BOOL  GenerateInternals(void) ;         // wrapper.c,h; wapi.C, readme.txt
BOOL  GenerateBuildFiles(void) ;        // Sources; makefile
BOOL  ExpandTo( HANDLE hFile, PCHAR pText, PCHAR pReplacement ) ;

/*
** Global data
*/
BOOL  fOverWrite    = FALSE ;
CHAR *pBaseFileName = NULL ;
CHAR *pFileNameExt  = NULL ;
CHAR *pFileStub     = NULL ;
WORD  fMachine      = 0 ;         // This is set in Scan.C


TEMPLATEDATA aTemplates[] =  
{
   { 
#include "wapic.tpl" 
      ,   "z%.c",          TPL_CONDITIONAL             },
   { 
#include "wapidef.tpl" 
      , "z%.def",          TPL_ALWAYS | TPL_MACHINE_SPECIFIC | TPL_EXPAND },
   { 
#include "wapih1.tpl"
      , "wapi.h",          TPL_ALWAYS | TPL_MACHINE_SPECIFIC| TPL_EXPAND     },
   { 
#include "wrapi386.tpl" 
      , "wrapem.asm",      TPL_INTERNAL | TPL_MACHINE_SPECIFIC | TPL_I386 | TPL_EXPAND   },
   { 
#include "wrapmips.tpl" 
      , "wrapem.s",        TPL_INTERNAL | TPL_MACHINE_SPECIFIC | TPL_MIPS | TPL_EXPAND    },
   { 
#include "wrapaxp.tpl" 
      , "wrapem.s",        TPL_INTERNAL | TPL_MACHINE_SPECIFIC | TPL_AXP | TPL_EXPAND     },
   { 
#include "wrapppc.tpl" 
      , "wrapem.s",        TPL_INTERNAL | TPL_MACHINE_SPECIFIC | TPL_PPC | TPL_EXPAND     },
   { 
#include "wrapperh.tpl" 
      , "wrapper.h",       TPL_INTERNAL                },
   { 
#include "wrapprc1.tpl" 
      , "wrapper.c",       TPL_INTERNAL                },
   { 
#include "wrapprc2.tpl" 
      , "wrapper.c",       TPL_APPEND                },
   { 
#include "wrapprc3.tpl" 
      , "wrapper.c",       TPL_APPEND                },
   { 
#include "readme.tpl" 
      , "readme.txt",      TPL_INTERNAL                },
   { 
#include "sources.tpl" 
      , "Sources",         TPL_CONDITIONAL | TPL_EXPAND},
   { 
#include "makefile.tpl" 
      , "Makefile",        TPL_INTERNAL                }
} ;                                                                              

#define TEMPLATES (sizeof(aTemplates)/sizeof(TEMPLATEDATA))      


typedef struct _apidata
{
   PCHAR pName ;
   DWORD dwOrdinal ;   
} APIDATA, PAPIDATA ;

APIDATA *apAPI = NULL ;
APIDATA *apData  = NULL ;

HGLOBAL hAPINameData = NULL ;
HGLOBAL hDataNameData = NULL ;

PCHAR pAPINameData = NULL ;
PCHAR pDataNameData = NULL ;

DWORD dwSizeAPINameData = 0 ;
DWORD dwOffsetAPINameData = 0 ;
DWORD dwSizeDataNameData = 0 ;
DWORD dwOffsetDataNameData = 0 ;

#define DATABLOCKSIZE    40000

HGLOBAL hAPINamePointers = NULL ;
HGLOBAL hDataNamePointers = NULL ;

DWORD dwAPIMaxIndex = 0 ;
DWORD dwDataMaxIndex = 0 ;

DWORD dwData = 0 ;
DWORD dwAPI  = 0 ;

#define INCREMENT   1024

/*
** DoScan
**
** Callback function for enumeratoin of API and Data names in DLL
**
*/
void DoScan(char *pcEntry,int iOrd,int fData, void *pv)
{
   int i;

   if (fData)
   {
      if ((dwSizeDataNameData - dwOffsetDataNameData) < (strlen(pcEntry)+1))
      {
         if ( hDataNameData != NULL )
         {
            GlobalUnlock( hDataNameData ) ;
            // realloc
            hDataNameData = GlobalReAlloc( hDataNameData, 
                                           dwSizeDataNameData + DATABLOCKSIZE,
                                           GMEM_ZEROINIT ) ;
         }
         else
         {
            // not yet allocated
            hDataNameData = GlobalAlloc( GHND, DATABLOCKSIZE ) ;
         }
         
         if ( hDataNameData != NULL )
         {
            dwSizeDataNameData = GlobalSize( hDataNameData ) ;
            pDataNameData = GlobalLock( hDataNameData) ;
         }
      }
      
      
      if ( 0 == (dwDataMaxIndex - dwData) )
      {
         if ( hDataNamePointers != NULL )
         {
            // realloc
            GlobalUnlock( hDataNamePointers) ;
            hDataNamePointers = GlobalReAlloc( hDataNamePointers, 
                                               ((dwDataMaxIndex + INCREMENT) * sizeof(APIDATA)),
                                               GMEM_ZEROINIT ) ;
         }
         else
         {
            // not yet allocated
            hDataNamePointers = GlobalAlloc( GHND, (INCREMENT * sizeof(APIDATA))) ;
         }
         
         if ( hDataNamePointers != NULL )
         {
            dwDataMaxIndex = GlobalSize(hDataNamePointers)/sizeof(APIDATA) ;
            apData = GlobalLock(hDataNamePointers) ;
         }
      }
      
      // add to list?
      if (apData != NULL && pDataNameData != NULL && dwData < dwDataMaxIndex )
      {
         apData[dwData].dwOrdinal = (DWORD)iOrd ;
         apData[dwData].pName = (PCHAR)(pDataNameData+dwOffsetDataNameData) ;
         memcpy( apData[dwData].pName, pcEntry, strlen(pcEntry)+1 ) ;
         dwData++ ;
         dwOffsetDataNameData += strlen(pcEntry)+1 ;
      }
      
   }
   else
   {
      if ((dwSizeAPINameData - dwOffsetAPINameData) < (strlen(pcEntry)+1))
      {
         if ( hAPINameData != NULL )
         {
            // realloc
            GlobalUnlock( hAPINameData) ;
            hAPINameData = GlobalReAlloc( hAPINameData, 
                                           dwSizeAPINameData + DATABLOCKSIZE,
                                           GMEM_ZEROINIT ) ;
         }
         else
         {
            // not yet allocated
            hAPINameData = GlobalAlloc( GHND, DATABLOCKSIZE ) ;
         }
         
         if ( hAPINameData != NULL )
         {
            dwSizeAPINameData = GlobalSize( hAPINameData ) ;
            pAPINameData = GlobalLock( hAPINameData) ;
         }
      }
      
      
      if ( 0 == (dwAPIMaxIndex - dwAPI) )
      {
         if ( hAPINamePointers != NULL )
         {
            // realloc
            GlobalUnlock( hAPINamePointers) ;
            hAPINamePointers = GlobalReAlloc( hAPINamePointers, 
                                               ((dwAPIMaxIndex + INCREMENT) * sizeof(APIDATA)),
                                               GMEM_ZEROINIT ) ;
         }
         else
         {
            // not yet allocated
            hAPINamePointers = GlobalAlloc( GHND, (INCREMENT * sizeof(APIDATA))) ;
         }
         
         if ( hAPINamePointers != NULL )
         {
            dwAPIMaxIndex = GlobalSize(hAPINamePointers)/sizeof(APIDATA) ;
            apAPI = GlobalLock(hAPINamePointers) ;
         }
      }
      
      // add to list?
      if (apAPI != NULL && pAPINameData != NULL && dwAPI < dwAPIMaxIndex)
      {
         apAPI[dwAPI].dwOrdinal = (DWORD)iOrd ;
         apAPI[dwAPI].pName = (PCHAR)(pAPINameData+dwOffsetAPINameData) ;
         memcpy( apAPI[dwAPI].pName, pcEntry, strlen(pcEntry)+1 ) ;
         dwAPI++ ;
         dwOffsetAPINameData += strlen(pcEntry)+1  ;
      }
   }
   return;
}






/*
** main
**
*/
int _CRTAPI1 main( int argc, char *argv[] ) 
{
   int   iLibraryName = 1 ;
   CHAR  achDLLName[256] ;
   CHAR  *pDLLName = achDLLName ;
   int   i,j ;
   DWORD dwOpenFlags = 0L ;
   HANDLE hFile ;
   DWORD  dwWritten ;
  
   if ( argc < 2 || argc > 3 )
   {
      // Dump helpful message
      printf( "\nAUTOWRAP [-f] dll-name\n-f\tUpdate: Overwrite WAPI.C.") ;
      printf( "\ndll-name\tThis is the name of the DLL that you wish to wrap.\n") ;
      return(1) ;
   }
   
   // Parse cmd line
   if ( argc == 3 && '-' == *argv[1] &&
         ('f' == *(argv[1]+1) || 'f' == *(argv[1]+1) ) )
   {
      fOverWrite = TRUE ;
      iLibraryName++ ;      
   }
   
   strcpy( pDLLName, argv[iLibraryName] ) ;
   
   // Read API and DATA from the DLL 
   if( !ScanDLL (argv[1], DoScan, NULL) )
    {fprintf(stderr,"ScanDLL error\n");
    return 1 ;}
   
   // find base name
   for( i=strlen(achDLLName), pBaseFileName = &achDLLName[i-1]; 
      i >= 0 && *pBaseFileName != '\\';
      pBaseFileName--, i-- )
      /* NULL BODY */ ;

   if( i < 0 )
      pBaseFileName++ ;

   pBaseFileName++ ;
   for( i=0; pBaseFileName[i] != '.'; i++ )
      /* NULL BODY */  ;
      
   pBaseFileName[i] = '\0' ;

   pFileStub = pBaseFileName+1 ;
   pFileNameExt = pFileStub+i ;

   if( !GenerateDirectoryTree() )
   {
      printf( "AutoWrap: Failed to create directory structure.\n" ) ;
      return 0 ;
            
   }
      
   for ( i=0; i < TEMPLATES; i++ )
   {
      CHAR  achFilename[256] ;
      PCHAR pFilename = achFilename ;  // no fear of running off...
      PCHAR pOrgFilename = aTemplates[i].pFilename ;
      PCHAR pText = aTemplates[i].pText ;

      // If this file is machine specific and we are looking at a binary
      // of the wrong machine type then skip this file
      if( PROCESSOR_SPECIFIC(aTemplates[i].wFlags) &&
          !(aTemplates[i].wFlags & fMachine) )
            continue ;
      
      // if this file is machine specific then we need to pre-pend the
      // the machine directory name to the filename
      if ( MACHINE_SPECIFIC(aTemplates[i].wFlags ) )
      {
         char *ptr = NULL ;;
         
         switch( fMachine )
         {
            case TPL_I386:
               ptr = I386_DIR ;
               break ;
               
            case TPL_MIPS:
               ptr = MIPS_DIR ;
               break ;
               
            case TPL_AXP:
               ptr = AXP_DIR ;
               break ;
               
            case TPL_PPC:
               ptr = PPC_DIR ;
               break ;
         }
         
         if( ptr )
         {
            strcpy( pFilename, ptr ) ;   
            pFilename += strlen(pFilename) ;            
         }
      }
      
      
      // expand file name  
      while ( *pOrgFilename )
      {
         if ( '%' == *pOrgFilename )
         {
            *pFilename = '\0' ;
            strcat( achFilename, pFileStub ) ;
            pFilename += strlen(pFileStub) ;
         }
         else
         {
            *pFilename = *pOrgFilename ;
            pFilename ++ ;
         }
            
         pOrgFilename++ ;   
      }
      *pFilename = '\0' ;
      
      // Open the file properly
      if ( aTemplates[i].wFlags & TPL_ALWAYS )
      {
         dwOpenFlags = CREATE_ALWAYS ;
      }
      
      if ( aTemplates[i].wFlags & TPL_CONDITIONAL ) 
      {
         if ( fOverWrite )
            dwOpenFlags = CREATE_ALWAYS ;
         else
            dwOpenFlags = CREATE_NEW ;
      }
      
      if ( aTemplates[i].wFlags & TPL_APPEND )  
      {
         dwOpenFlags = OPEN_EXISTING ;
      }
      
      hFile = CreateFile( achFilename,
                          GENERIC_READ | GENERIC_WRITE,
                          FILE_SHARE_READ,
                          NULL,
                          dwOpenFlags,
                          FILE_ATTRIBUTE_NORMAL,
                          (HANDLE)NULL ) ;
                     
      if ( hFile != INVALID_HANDLE_VALUE )                          
      {
         // if appending then seek to EOF
         if ( aTemplates[i].wFlags & TPL_APPEND )  
         {
            SetFilePointer( hFile, 0, NULL, FILE_END ) ;
         }
         
         if ( aTemplates[i].wFlags & TPL_EXPAND )  
         {
            // Expand the template      
            if ( !ExpandTo( hFile, pText, pFileStub) )
            {
               printf( "AutoWrap: Failed to expand template for %s.\n",
                        aTemplates[i].pFilename ) ;
            }
         }
         else
         {
            CHAR achBuffer[1024] ;
            
            j = 0 ;
            
            // just copy it to out
            while ( *pText )
            {
               if (sizeof(achBuffer) == j)
               {
                  WriteFile( hFile, achBuffer, sizeof(achBuffer),
                        &dwWritten, NULL ) ;
                  
                  j = 0 ;
                  
                  if( sizeof(achBuffer) != dwWritten )
                  {
                     printf( "AutoWrap: Failed to copy template to %s.\n",
                           aTemplates[i].pFilename ) ;
                     break ;
                  }
               }
               
               achBuffer[j] = *pText ;
               j++ ;
               pText++ ;
            }
            
            // left overs?
            if (0 != j)
            {
               WriteFile( hFile, achBuffer, j,
                     &dwWritten, NULL ) ;
               
               if( j != (int)dwWritten )
               {
                  printf( "AutoWrap: Failed to copy template to %s.\n",
                        aTemplates[i].pFilename ) ;
               }
               
            }
         }
         FlushFileBuffers(hFile) ;
         CloseHandle( hFile ) ;            
      }
      else
      {
         if ( !(aTemplates[i].wFlags & TPL_CONDITIONAL) )
            printf( "AutoWrap: Unable to open %s.\n", achFilename ) ;
      }
   }

   return 0 ;
}

DWORD dwBuffered = 0 ;
char  achBuffer[BUFFER_SIZE] ;
char *pBuffer = achBuffer ;

void FlushBuff( HANDLE hFile )
{
   DWORD dwWritten ;
   
   if ( dwBuffered != 0 )
      if( hFile != INVALID_HANDLE_VALUE )
      {
         WriteFile( hFile, achBuffer, dwBuffered, &dwWritten, NULL ) ;
         dwBuffered = 0 ;
		 pBuffer = achBuffer ;
      }
}

void WriteString( HANDLE hFile, LPSTR lpText )
{
   while ( '\0' != *lpText )
   {
      if ( BUFFER_SIZE == dwBuffered )
         FlushBuff(hFile);
      *pBuffer++ = *lpText++ ;
      dwBuffered++;
   }
}

void WriteChar( HANDLE hFile, LPSTR lpText, int count )
{
   char    ch;

   while ( count-- )
   {
      if ( BUFFER_SIZE == dwBuffered )
         FlushBuff(hFile);
         
      *pBuffer++ = *lpText++;
      dwBuffered++;
   }
}

/*
** ExpandLineTo
**
** Template expansions
**
** %s       filename stub
** %l       Basename of library
** %e       Extension of library
** %a       real apiname
** %d       real dataname
** %A       modified apiname
** %D       modified dataname
**
**       modified names are '?' and '@' replaced with '_'.  This is because
**       the alpha assembler can't handle these characters in names... :-(
**
** %i       index
** %c       count of API names
** %o       ordinal of API
** %O       ordinal of Data
*/
BOOL ExpandLineTo( HANDLE hFile, PCHAR pText, PCHAR pReplacement, DWORD dwIndex )
{
//   printf("ExpandLineTo: %s \n",pText);

   while( *pText )
   {
      if( '%' == *pText )   
      {
         pText++ ;
         
         switch( *pText )
         {
            default:
               printf( "AutoWrap: Syntax error in repeated template line.\n" ) ;
               break ;
               
            case 'a':
               WriteString( hFile, apAPI[dwIndex].pName ) ;
               break ;
               
            case 'A':
               {
                  char temp[50], *ptemp = temp ;
                  char *pname  = apAPI[dwIndex].pName ;
                  
                  while( *pname )
                  {
                     switch( *pname )
                     {
                        case '?':
                        case '@':
                           *ptemp = '_' ;                       
                           break ;
                           
                        default:
                           *ptemp = *pname ;
                           break ;
                     }
                     
                     ptemp++ ;
                     pname++ ;
                  }
                  
                  *ptemp = '\0' ;
                  
                  WriteString( hFile, temp ) ;
               
               }
            
               break ;
               
            case 'd':
               WriteString( hFile, apData[dwIndex].pName ) ;
               break ;
               
            case 'D':
               {
                  char temp[50], *ptemp = temp ;
                  char *pname  = apData[dwIndex].pName ;
                  
                  while( *pname )
                  {
                     switch( *pname )
                     {
                        case '?':
                        case '@':
                           *ptemp = '_' ;                       
                           break ;
                           
                        default:
                           *ptemp = *pname ;
                           break ;
                     }
                     
                     ptemp++ ;
                     pname++ ;
                  }
                  
                  *ptemp = '\0' ;
                  
                  WriteString( hFile, temp ) ;
               }
            
               break ;
               
            case 'l':
               WriteString( hFile, pBaseFileName ) ;
               break ;
               
            case 'e':
               WriteString( hFile, pFileNameExt  ) ;
               break ;
               
            case 's':
               WriteString( hFile, pReplacement ) ;
               break ;
               
            case 'c':
            {
               char achText[12] ;
               sprintf( achText, "%ld", dwAPI ) ;
               WriteString( hFile, achText  ) ;
            }
            break ;
            
            case 'i':
            {
               char achText[12] ;
               sprintf( achText, "%ld", dwIndex ) ;
               WriteString( hFile, achText  ) ;
            }
            break ;
               
            case 'o':
            {
               char achText[12] ;
               sprintf( achText, "%ld", apAPI[dwIndex].dwOrdinal ) ;
               WriteString( hFile, achText  ) ;
            }
            break ;
               
            case 'O':
            {
               char achText[12] ;
               sprintf( achText, "%ld", apData[dwIndex].dwOrdinal ) ;
               WriteString( hFile, achText  ) ;
            }
            break ;
               
         }
      }
      else
         WriteChar( hFile, pText, 1 )  ;
         
      pText++ ;
   }
   return TRUE ; ;
      
}
/*
** ExpandTo
**
** Template expansions
**
** %s       filename stub
** %l       Basename of library
** %e       Extension of library
** %a       apiname
** %c       count of API names
** %a...\n  take and expand rest of line repeatedly for each API available
**          So %a"%a",\n would be used to create apinames list.
** %d...\n  take and expand rest of line repeatedly for each API available
**          So %d"%d",\n would be used to create data names list.
** %ifilename\n
**          Appends the given file to the current one.   
**
*/
BOOL ExpandTo( HANDLE hFile, PCHAR pText, PCHAR pReplacement )
{
   
//   printf("ExpandTo: %s %s\n",pText,pReplacement);

   while( *pText )
   {
      if( '%' == *pText )   
      {
         pText++ ;
         
         switch( *pText )
         {
            default:
               printf( "AutoWrap: Syntax error in template.\n" ) ;
               break ;
               
            case 's':
               WriteString( hFile, pReplacement ) ;
               break ;
               
            case 'l':
               WriteString( hFile, pBaseFileName ) ;
               break ;
               
            case 'e':
               WriteString( hFile, pFileNameExt) ;
               break ;
               
            case 'c':
            {
               char achText[12] ;
               sprintf( achText, "%ld", dwAPI ) ;
               WriteString( hFile, achText  ) ;
            }
            break ;
               
            case 'd':
            case 'a':
            {
               char  achNewTemp[256] ;
               PCHAR p = achNewTemp ;
               DWORD dwIndex ;
               DWORD dwCnt = ('a' == *pText) ? dwAPI : dwData ;
               
               // Copy to '\n' into achNewTemp
               pText++ ;
               while( '\n' != *pText )
               {
                  *p = *pText ;
                  pText++ ;
                  p++ ;
               }
               *p++ = '\r' ;
               *p++ = '\n' ;
               *p++ = '\0' ;
               
               for( dwIndex = 0; dwIndex < dwCnt; dwIndex++ )
               {
                  ExpandLineTo( hFile, achNewTemp, pReplacement, dwIndex ) ;
               }
               
            }
            break ;
            
            case 'i':
            {
               char  achNewTemp[256] ;
               PCHAR p = achNewTemp ;
               DWORD dwRead, dwTemp ;
               HANDLE hAppendFile ;
               
               // Flush write buffer first
               FlushBuff(hFile) ;
               
               // Copy to '\n' into achNewTemp
               pText++ ;
               while( '\n' != *pText )
               {
                  *p = *pText ;
                  pText++ ;
                  p++ ;
               }
               *p++ = '\0' ;
               
               // achNewTemp is the filename to open
               hAppendFile = CreateFile( achNewTemp,
                          GENERIC_READ,
                          FILE_SHARE_READ,
                          NULL,
                          OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL,
                          (HANDLE)NULL ) ;
                     
               if ( hFile != INVALID_HANDLE_VALUE )                          
               {
                  dwRead = 1 ;
                  // copy the file
                  while( dwRead )
                  {
                     ReadFile( hAppendFile, achNewTemp, sizeof(achNewTemp), &dwRead, NULL) ;
                     WriteFile( hFile, achNewTemp, dwRead, &dwTemp, NULL ) ;
                  }
                  CloseHandle(hAppendFile) ;
               }
               
            }
            break ;
         }
      }
      else
      {
         WriteChar( hFile, pText, 1) ;
      }
      pText++ ;
   }
   
   FlushBuff( hFile ) ;
   
   return TRUE ; ;
   
}


/*
** GenerateDirectoryTree
**
** Makes sure that the following directory structure exists off of current 
** directory.
**
** Current ----- OBJ
**          |
**           --- I386
**          |
**           --- MIPS
**          |
**           --- ALPHA
**          |
**           --- PPC
**
*/
BOOL GenerateDirectoryTree()
{
   BOOL bRet = TRUE ;
  
   if( !CreateDirectory( "obj", NULL ) )
   {
      // already exist?
      if( ERROR_ALREADY_EXISTS == GetLastError() )
         bRet = TRUE ;
      else
         bRet = FALSE ;   
   }
   
   if( !CreateDirectory( "i386", NULL ) )
   {
      // already exist?
      if( ERROR_ALREADY_EXISTS == GetLastError() )
         bRet &= TRUE ;
      else
         bRet = FALSE ;   
   }
   
   if( !CreateDirectory( "Mips", NULL ) )
   {
      // already exist?
      if( ERROR_ALREADY_EXISTS == GetLastError() )
         bRet &= TRUE ;
      else
         bRet = FALSE ;   
   }
   
   if( !CreateDirectory( "Alpha", NULL ) )
   {
      // already exist?
      if( ERROR_ALREADY_EXISTS == GetLastError() )
         bRet &= TRUE ;
      else
         bRet = FALSE ;   
   }
   if( !CreateDirectory( "ppc", NULL ) )
   {
      // already exist?
      if( ERROR_ALREADY_EXISTS == GetLastError() )
         bRet &= TRUE ;
      else
         bRet = FALSE ;   
   }
   
   return bRet ; ;
      
}