/* $Id: drivers.c,v 1.1 2003/04/13 03:24:27 hyperion Exp $
*/
/*
 * COPYRIGHT:   See COPYING in the top level directory
 * LICENSE:     See LGPL.txt in the top level directory
 * PROJECT:     ReactOS system libraries
 * FILE:        reactos/lib/epsapi/enum/drivers.c
 * PURPOSE:     Enumerate system modules
 * PROGRAMMER:  KJK::Hyperion <noog@libero.it>
 * UPDATE HISTORY:
 *              02/04/2003: Created
 *              12/04/2003: internal PSAPI renamed EPSAPI (Extended PSAPI) and
 *                          isolated in its own library to clear the confusion
 *                          and improve reusability
 */

#include <ddk/ntddk.h>
#include <debug.h>
#include <stddef.h>

#include <epsapi.h>

NTSTATUS
NTAPI
PsaEnumerateSystemModules
(
 IN PSYSMOD_ENUM_ROUTINE Callback,
 IN OUT PVOID CallbackContext
)
{
 register NTSTATUS nErrCode = STATUS_SUCCESS;
 PSYSTEM_MODULES psmModules = NULL;

#if 0
 __try
 {
#endif
  do
  {
   /* capture the system modules */
   nErrCode = PsaCaptureSystemModules(&psmModules);
   
   if(!NT_SUCCESS(nErrCode))
    /* failure */
    break;
 
   /* walk the system modules */
   nErrCode = PsaWalkSystemModules(psmModules, Callback, CallbackContext);
  }
  while(0);
#if 0
 }
 __finally
 {
#endif
  /* free the capture */
  PsaFreeCapture(psmModules);
#if 0
 }
#endif
 
 /* return the last status */
 return nErrCode;
}

NTSTATUS
NTAPI
PsaCaptureSystemModules
(
 OUT PSYSTEM_MODULES * SystemModules
)
{
 SIZE_T nSize = 0;
 register NTSTATUS nErrCode;
 register PSYSTEM_MODULES psmModules = (PSYSTEM_MODULES)&nSize;

#if 0
 __try
 {
#endif
  do
  {
   /* initial probe. We just get the count of system modules */
   nErrCode = NtQuerySystemInformation
   (
    SystemModuleInformation,
    psmModules,
    sizeof(nSize),
    NULL
   );

   if(nErrCode != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(nErrCode))
   {
    /* failure */
    DPRINT(FAILED_WITH_STATUS, "NtQuerySystemInformation", nErrCode);
    break;
   }

   /* RATIONALE: the loading of a system module is a rare occurrence. To
      minimize memory operations that could be expensive, or fragment the
      pool/heap, we try to determine the buffer size in advance, knowing that
      the number of elements is unlikely to change */
   nSize =
    sizeof(*psmModules) +
    (psmModules->Count - 1) * sizeof(SYSTEM_MODULE_INFORMATION);

   psmModules = NULL;

   do
   {
    register void * pTmp;
  
    /* free the buffer, and reallocate it to the new size. RATIONALE: since we
       ignore the buffer's content at this point, there's no point in a realloc,
       that could end up copying a large chunk of data we'd discard anyway */
    PsaiFree(psmModules);
    pTmp = PsaiMalloc(nSize);
    
    if(pTmp == NULL)
    {
     /* failure */
     nErrCode = STATUS_NO_MEMORY;
     DPRINT(FAILED_WITH_STATUS, "PsaiMalloc", nErrCode);
     break;
    }

    psmModules = pTmp;

    /* query the information */
    nErrCode = NtQuerySystemInformation
    (
     SystemModuleInformation,
     psmModules,
     nSize,
     NULL
    );

    /* double the buffer for the next loop */
    nSize += nSize;
   }
   /* repeat until the buffer is big enough */
   while(nErrCode == STATUS_INFO_LENGTH_MISMATCH);

   if(!NT_SUCCESS(nErrCode))
   {
    /* failure */
    DPRINT(FAILED_WITH_STATUS, "NtQuerySystemInformation", nErrCode);
    break;
   }

   /* success */
   *SystemModules = psmModules;

   nErrCode = STATUS_SUCCESS;
  }
  while(0);
#if 0
 }
 __finally
 {
#endif
  /* in case of failure, free the buffer */
  if(!NT_SUCCESS(nErrCode))
   PsaiFree(psmModules);
#if 0
 }
#endif

 /* return the last status */
 return (nErrCode);
}

NTSTATUS
NTAPI
PsaWalkSystemModules
(
 IN PSYSTEM_MODULES SystemModules,
 IN PSYSMOD_ENUM_ROUTINE Callback,
 IN OUT PVOID CallbackContext
)
{
 register NTSTATUS nErrCode;
 register SIZE_T i;

 /* repeat until all modules have been returned */
 for(i = 0; i < SystemModules->Count; ++ i)
 {
  /* return current module to the callback */
  nErrCode = Callback(&(SystemModules->Modules[i]), CallbackContext);
  
  if(!NT_SUCCESS(nErrCode))
   /* failure */
   return nErrCode;
 }

 /* success */
 return STATUS_SUCCESS;
}

PSYSTEM_MODULE_INFORMATION
FASTCALL
PsaWalkFirstSystemModule
(
 IN PSYSTEM_MODULES SystemModules
)
{ 
 return &(SystemModules->Modules[0]);
}

PSYSTEM_MODULE_INFORMATION
FASTCALL
PsaWalkNextSystemModule
(
 IN PSYSTEM_MODULE_INFORMATION CurrentSystemModule
)
{
 return (PSYSTEM_MODULE_INFORMATION)
 (
  (ULONG_PTR)CurrentSystemModule +
  (
   offsetof(SYSTEM_MODULES, Modules[1]) -
   offsetof(SYSTEM_MODULES, Modules[0])
  )
 );
}

/* EOF */
