/***************************************************************************
 *                                                                         *
 *   LOGMODS.C                                                             *
 *                                                                         *
 *   Copyright (c) 1987-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is a log of modules loaded into the WGSERVER memory space for    *
 *   NT/95 only.                                                           *
 *                                                                         *
 *                                        - R. Skurnick  7/11/96           *
 *                                                                         *
 ***************************************************************************/

#include <windows.h>
#include "gcomm.h"

#define FILREV "$Revision: 3 $"

typedef struct {                   // region info
     VOID *rgnAddress;             //   address of mem region
     DWORD rgnProtection;          //   protection attribute
     DWORD rgnSize;                //   size
     DWORD rgnType;                //   type (Free,Image,Mapped,Private)
     DWORD rgnBlocks;              //   number of blocks
     DWORD rgnGuardBlks;           //   blocks with "guard" attribute
     GBOOL rgnIsStack;             //   is region a thread stack
} RGNINFO;

static GBOOL getRegionInfo(VOID *memStart,RGNINFO *rgnInfo);
static VOID addModuleInfo(FILE *logfp,CHAR *modFileName);
static VOID walkRegion(RGNINFO *rgnInfo);
static DWORD fixVirtualQuery (LPVOID lpvAddress,
          PMEMORY_BASIC_INFORMATION pmbiBuffer,DWORD cbLength);


static CHAR curDirectory[260];

VOID
logmods(VOID)                 /* record all memory regions to log file     */
{
     FILE *logfp;
     VOID *memStart=0x0;
     RGNINFO rgnInfo;
     CHAR modFileName[512];

     if ((logfp=fopen("wgserver.out",FOPWA)) == NULL) {
          return;
     }
     if (GetCurrentDirectory(260,curDirectory) != 0) {
          strcat(curDirectory,"\\");
     }
     fprintf(logfp,"Region   Module                                    Date     Time         Bytes\n");
     fprintf(logfp,"-------- ----------------------------------------- -------- -------- ---------\n");
     while (getRegionInfo(memStart,&rgnInfo)) {
          fprintf(logfp,"%08X ",rgnInfo.rgnAddress);
          if (rgnInfo.rgnAddress == GetProcessHeap()) {
               fprintf(logfp,"%-41s\n","Default Process Heap");
          }
          else if (rgnInfo.rgnIsStack) {
               fprintf(logfp,"%-41s\n","Thread Stack");
          }
          else if (rgnInfo.rgnAddress != NULL) {
               if (GetModuleFileName((HINSTANCE)rgnInfo.rgnAddress,modFileName,
                                      sizeof(modFileName)) > 0) {
                    addModuleInfo(logfp,modFileName);
               }
               else {
                    fprintf(logfp,"\n");
               }
          }
          else {
               fprintf(logfp,"\n");
          }
          memStart=(CHAR*)rgnInfo.rgnAddress+rgnInfo.rgnSize;
     }
     fclose(logfp);
}

static GBOOL                       /*   TRUE=success,FALSE=no more regions */
getRegionInfo(                     /* get info for next region             */
VOID *memStart,                    /*   beginning of region                */
RGNINFO *rgnInfo)                  /*   region info to fill in             */
{
     MEMORY_BASIC_INFORMATION memInfo;

     memset(rgnInfo,0,sizeof(*rgnInfo));
     if (fixVirtualQuery(memStart,&memInfo,sizeof(memInfo)) != sizeof(memInfo)) {
          return(FALSE);
     }
     if (memInfo.State == MEM_FREE) {
          rgnInfo->rgnAddress=memInfo.BaseAddress;
          rgnInfo->rgnProtection=memInfo.AllocationProtect;
          rgnInfo->rgnSize=memInfo.RegionSize;
          rgnInfo->rgnType=MEM_FREE;
          rgnInfo->rgnBlocks=0;
          rgnInfo->rgnGuardBlks=0;
          rgnInfo->rgnIsStack=FALSE;
          return(TRUE);
     }
     rgnInfo->rgnAddress=memInfo.AllocationBase;
     rgnInfo->rgnProtection=memInfo.AllocationProtect;
     walkRegion(rgnInfo);
     return(TRUE);
}

static VOID
walkRegion(                        /* walk through region count blocks&size*/
RGNINFO *rgnInfo)                  /*   region info to fill in             */
{
     MEMORY_BASIC_INFORMATION memInfo;
     VOID *memStart;
     DWORD blkProtAttr[4]={0};

     memStart=rgnInfo->rgnAddress;
     rgnInfo->rgnType=MEM_PRIVATE;
     while (fixVirtualQuery(memStart,&memInfo,sizeof(memInfo)) == sizeof(memInfo)){
          if (memInfo.AllocationBase != rgnInfo->rgnAddress) {
               break;
          }
          blkProtAttr[rgnInfo->rgnBlocks%4]=
                    (memInfo.State == MEM_RESERVE) ? 0 : memInfo.Protect;
          rgnInfo->rgnBlocks++;
          rgnInfo->rgnSize+=memInfo.RegionSize;
          if (memInfo.Protect&PAGE_GUARD) {
               rgnInfo->rgnGuardBlks++;
          }
          if (rgnInfo->rgnType == MEM_PRIVATE) {
               rgnInfo->rgnType=memInfo.Type;
          }
          memStart=(CHAR *)memStart+memInfo.RegionSize;
     }
     if (rgnInfo->rgnGuardBlks > 0) {   // NT stack has a guard page
          rgnInfo->rgnIsStack=TRUE;
     }
     else if (rgnInfo->rgnBlocks >= 4) {
          // Win95 has sequence reserved,PAGE_NOACCESS,PAGE_READWRITE,reserved
          // at the end of stack region
          if (blkProtAttr[(rgnInfo->rgnBlocks-1)%4] == 0
           && blkProtAttr[(rgnInfo->rgnBlocks-2)%4] == PAGE_READWRITE
           && blkProtAttr[(rgnInfo->rgnBlocks-3)%4] == PAGE_NOACCESS
           && blkProtAttr[(rgnInfo->rgnBlocks-4)%4] == 0) {
               rgnInfo->rgnIsStack=TRUE;
          }
     }
}

static VOID
addModuleInfo(                     /* add module date, time & size info    */
FILE *logfp,                       /*   file to print to                   */
CHAR *modFileName)                 /*   module name to check               */
{
     INT tstlen;
     WIN32_FIND_DATA findData;
     FILETIME fileTime;
     SYSTEMTIME sysTime;
     CHAR *chkptr,*tstptr;
     HANDLE hFF;

     if ((hFF=FindFirstFile(modFileName,&findData)) != INVALID_HANDLE_VALUE) {
          FindClose(hFF);
          for (chkptr=curDirectory,tstptr=modFileName ;
                                                  *chkptr != '\0' ; ) {
               if (*chkptr != *tstptr) {
                    break;
               }
               chkptr++;
               tstptr++;
          }
          if (*chkptr == '\0') {   // the strings match
               tstlen=lstrlen(tstptr);
          }
          else {
               tstlen=lstrlen(modFileName);
               tstptr=modFileName;
          }
          if (tstlen > 41) {          // truncate the prefix and leave the end
               tstptr+=tstlen;
               tstptr-=41;
               *tstptr='.';
               *(tstptr+1)='.';
          }
          fprintf(logfp,"%-41s",tstptr);
          FileTimeToLocalFileTime(&findData.ftLastWriteTime,&fileTime);
          FileTimeToSystemTime(&fileTime,&sysTime);
          fprintf(logfp,"  %02d/%02d/%02d %2d:%02d:%02d %9lu\n",
                   sysTime.wMonth,sysTime.wDay,
                   sysTime.wYear-(sysTime.wYear > 1999 ? 2000 : 1900),
                   sysTime.wHour,sysTime.wMinute,sysTime.wSecond,
                   findData.nFileSizeLow);
     }
     else {
          fprintf(logfp,"%s - Unable to find file\n",modFileName);
     }
}

static DWORD
fixVirtualQuery (                  /* compensate for bug in NT VirtualQuery*/
LPVOID lpvAddress,
PMEMORY_BASIC_INFORMATION pmbiBuffer,
DWORD cbLength)
{
     DWORD dwRetVal=VirtualQuery(lpvAddress,pmbiBuffer,cbLength);
     if (dwRetVal == cbLength) {
          if (((DWORD)pmbiBuffer->AllocationBase%0x1000) == 0xFFF) {
               // If the AllocationBase ends with 0xFFF, it is 1 byte off.
               pmbiBuffer->AllocationBase=
                    (PVOID)((PBYTE)pmbiBuffer->AllocationBase+1);
          }
          if ((pmbiBuffer->RegionSize%0x1000) == 0xFFF) {
               // If the RegionSize ends with 0xFFF, the size is 1 byte off.
               pmbiBuffer->RegionSize++;
          }
          if (pmbiBuffer->State != MEM_FREE
           && pmbiBuffer->AllocationProtect == 0) {
               // If the the region is not free and the AllocationProtect is 0,
               // AllocationProtect should be PAGE_READONLY.
               pmbiBuffer->AllocationProtect=PAGE_READONLY;
          }
     }
     return(dwRetVal);
}

