You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2780 lines
79 KiB
2780 lines
79 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fileenum.c
|
|
|
|
Abstract:
|
|
|
|
Implements a set of APIs to enumerate a file system using Win32 APIs.
|
|
|
|
Author:
|
|
|
|
20-Oct-1999 Ovidiu Temereanca (ovidiut) - File creation.
|
|
|
|
Revision History:
|
|
|
|
<alias> <date> <comments>
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
// None
|
|
|
|
#define DBG_FILEENUM "FileEnum"
|
|
|
|
//
|
|
// Strings
|
|
//
|
|
|
|
#define S_FILEENUM "FILEENUM"
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
#define pFileAllocateMemory(Size) PmGetMemory (g_FileEnumPool,Size)
|
|
#define pFileFreeMemory(Buffer) if (Buffer) PmReleaseMemory (g_FileEnumPool, (PVOID)Buffer)
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
PMHANDLE g_FileEnumPool;
|
|
static INT g_FileEnumRefs;
|
|
|
|
//
|
|
// Macro expansion list
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Macro expansion definition
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Code
|
|
//
|
|
|
|
|
|
BOOL
|
|
FileEnumInitialize (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FileEnumInitialize initializes this library.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if the init was successful.
|
|
FALSE if not. GetLastError() returns extended error info.
|
|
|
|
--*/
|
|
|
|
{
|
|
g_FileEnumRefs++;
|
|
|
|
if (g_FileEnumRefs == 1) {
|
|
g_FileEnumPool = PmCreateNamedPool (S_FILEENUM);
|
|
}
|
|
|
|
return g_FileEnumPool != NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
FileEnumTerminate (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FileEnumTerminate is called to free resources used by this lib.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
MYASSERT (g_FileEnumRefs > 0);
|
|
g_FileEnumRefs--;
|
|
|
|
if (!g_FileEnumRefs) {
|
|
if (g_FileEnumPool) {
|
|
PmDestroyPool (g_FileEnumPool);
|
|
g_FileEnumPool = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumFirstDrive enumerates the first fixed drive root
|
|
|
|
Arguments:
|
|
|
|
DriveEnum - Receives info about the first fixed drive root
|
|
|
|
Return Value:
|
|
|
|
TRUE if a drive root was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
EnumFirstDriveA (
|
|
OUT PDRIVE_ENUMA DriveEnum,
|
|
IN UINT WantedDriveTypes
|
|
)
|
|
{
|
|
DWORD len;
|
|
|
|
len = GetLogicalDriveStringsA (0, NULL);
|
|
if (len) {
|
|
DriveEnum->AllLogicalDrives = pFileAllocateMemory ((len + 1) * sizeof (CHAR));
|
|
if (DriveEnum->AllLogicalDrives) {
|
|
GetLogicalDriveStringsA (len, DriveEnum->AllLogicalDrives);
|
|
DriveEnum->DriveName = NULL;
|
|
DriveEnum->WantedDriveTypes = WantedDriveTypes;
|
|
return EnumNextDriveA (DriveEnum);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
EnumFirstDriveW (
|
|
OUT PDRIVE_ENUMW DriveEnum,
|
|
IN UINT WantedDriveTypes
|
|
)
|
|
{
|
|
DWORD len;
|
|
|
|
len = GetLogicalDriveStringsW (0, NULL);
|
|
if (len) {
|
|
DriveEnum->AllLogicalDrives = pFileAllocateMemory ((len + 1) * sizeof (WCHAR));
|
|
if (DriveEnum->AllLogicalDrives) {
|
|
GetLogicalDriveStringsW (len, DriveEnum->AllLogicalDrives);
|
|
DriveEnum->DriveName = NULL;
|
|
DriveEnum->WantedDriveTypes = WantedDriveTypes;
|
|
return EnumNextDriveW (DriveEnum);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumNextDrive enumerates the next fixed drive
|
|
|
|
Arguments:
|
|
|
|
DriveEnum - Specifies info about the previous fixed drive root; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a new drive root was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
EnumNextDriveA (
|
|
IN OUT PDRIVE_ENUMA DriveEnum
|
|
)
|
|
{
|
|
do {
|
|
if (!DriveEnum->DriveName) {
|
|
DriveEnum->DriveName = DriveEnum->AllLogicalDrives;
|
|
} else {
|
|
// Since DriveEnum->DriveName is not NULL, GetEndOfStringA will
|
|
// not return NULL so...
|
|
DriveEnum->DriveName = GetEndOfStringA (DriveEnum->DriveName) + 1; //lint !e613
|
|
}
|
|
if (*DriveEnum->DriveName == 0) {
|
|
AbortEnumDriveA (DriveEnum);
|
|
return FALSE;
|
|
}
|
|
|
|
DriveEnum->DriveType = GetDriveTypeA (DriveEnum->DriveName);
|
|
|
|
switch (DriveEnum->DriveType) {
|
|
case DRIVE_UNKNOWN:
|
|
DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
|
|
break;
|
|
case DRIVE_NO_ROOT_DIR:
|
|
DriveEnum->DriveType = DRIVEENUM_NOROOTDIR;
|
|
break;
|
|
case DRIVE_REMOVABLE:
|
|
DriveEnum->DriveType = DRIVEENUM_REMOVABLE;
|
|
break;
|
|
case DRIVE_FIXED:
|
|
DriveEnum->DriveType = DRIVEENUM_FIXED;
|
|
break;
|
|
case DRIVE_REMOTE:
|
|
DriveEnum->DriveType = DRIVEENUM_REMOTE;
|
|
break;
|
|
case DRIVE_CDROM:
|
|
DriveEnum->DriveType = DRIVEENUM_CDROM;
|
|
break;
|
|
case DRIVE_RAMDISK:
|
|
DriveEnum->DriveType = DRIVEENUM_RAMDISK;
|
|
break;
|
|
default:
|
|
DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
|
|
}
|
|
|
|
} while (!(DriveEnum->DriveType & DriveEnum->WantedDriveTypes));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
EnumNextDriveW (
|
|
IN OUT PDRIVE_ENUMW DriveEnum
|
|
)
|
|
{
|
|
do {
|
|
if (!DriveEnum->DriveName) {
|
|
DriveEnum->DriveName = DriveEnum->AllLogicalDrives;
|
|
} else {
|
|
DriveEnum->DriveName = GetEndOfStringW (DriveEnum->DriveName) + 1;
|
|
}
|
|
if (*DriveEnum->DriveName == 0) {
|
|
AbortEnumDriveW (DriveEnum);
|
|
return FALSE;
|
|
}
|
|
|
|
DriveEnum->DriveType = GetDriveTypeW (DriveEnum->DriveName);
|
|
|
|
switch (DriveEnum->DriveType) {
|
|
case DRIVE_UNKNOWN:
|
|
DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
|
|
break;
|
|
case DRIVE_NO_ROOT_DIR:
|
|
DriveEnum->DriveType = DRIVEENUM_NOROOTDIR;
|
|
break;
|
|
case DRIVE_REMOVABLE:
|
|
DriveEnum->DriveType = DRIVEENUM_REMOVABLE;
|
|
break;
|
|
case DRIVE_FIXED:
|
|
DriveEnum->DriveType = DRIVEENUM_FIXED;
|
|
break;
|
|
case DRIVE_REMOTE:
|
|
DriveEnum->DriveType = DRIVEENUM_REMOTE;
|
|
break;
|
|
case DRIVE_CDROM:
|
|
DriveEnum->DriveType = DRIVEENUM_CDROM;
|
|
break;
|
|
case DRIVE_RAMDISK:
|
|
DriveEnum->DriveType = DRIVEENUM_RAMDISK;
|
|
break;
|
|
default:
|
|
DriveEnum->DriveType = DRIVEENUM_UNKNOWN;
|
|
}
|
|
|
|
} while (!(DriveEnum->DriveType & DriveEnum->WantedDriveTypes));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AbortEnumDrive aborts enumeration of fixed drives
|
|
|
|
Arguments:
|
|
|
|
DriveEnum - Specifies info about the previous fixed drive;
|
|
receives a "clean" context
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
AbortEnumDriveA (
|
|
IN OUT PDRIVE_ENUMA DriveEnum
|
|
)
|
|
{
|
|
if (DriveEnum->AllLogicalDrives) {
|
|
pFileFreeMemory (DriveEnum->AllLogicalDrives);
|
|
DriveEnum->AllLogicalDrives = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AbortEnumDriveW (
|
|
IN OUT PDRIVE_ENUMW DriveEnum
|
|
)
|
|
{
|
|
if (DriveEnum->AllLogicalDrives) {
|
|
pFileFreeMemory (DriveEnum->AllLogicalDrives);
|
|
DriveEnum->AllLogicalDrives = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGetFileEnumInfo is a private function that validates and translates the enumeration info
|
|
in an internal form that's more accessible to the enum routines
|
|
|
|
Arguments:
|
|
|
|
FileEnumInfo - Receives the enum info
|
|
EncodedPathPattern - Specifies the encoded dir pattern (encoded as defined by the
|
|
ParsedPattern functions)
|
|
EnumDirs - Specifies TRUE if directories should be returned during the enumeration
|
|
(if they match the pattern); a directory is returned before any of its
|
|
subdirs or files
|
|
ContainersFirst - Specifies TRUE if directories should be returned before any of its
|
|
files or subdirs; used only if EnumDirs is TRUE
|
|
FilesFirst - Specifies TRUE if a dir's files should be returned before dir's subdirs;
|
|
this parameter decides the enum order between files and subdirs
|
|
for each directory
|
|
DepthFirst - Specifies TRUE if the current subdir of any dir should be fully enumerated
|
|
before going to the next subdir; this parameter decides if the tree
|
|
traversal is depth-first (TRUE) or width-first (FALSE)
|
|
MaxSubLevel - Specifies the maximum sub-level of a dir that is to be enumerated, relative to
|
|
the root; if -1, all sub-levels are enumerated
|
|
UseExclusions - Specifies TRUE if exclusion APIs should be used to determine if certain
|
|
paths/files are excluded from enumeration; this slows down the speed
|
|
|
|
Return Value:
|
|
|
|
TRUE if all params are valid; in this case, FileEnumInfo is filled with the corresponding
|
|
info.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pGetFileEnumInfoA (
|
|
OUT PFILEENUMINFOA FileEnumInfo,
|
|
IN PCSTR EncodedPathPattern,
|
|
IN BOOL EnumDirs,
|
|
IN BOOL ContainersFirst,
|
|
IN BOOL FilesFirst,
|
|
IN BOOL DepthFirst,
|
|
IN DWORD MaxSubLevel,
|
|
IN BOOL UseExclusions
|
|
)
|
|
{
|
|
FileEnumInfo->PathPattern = ObsCreateParsedPatternA (EncodedPathPattern);
|
|
if (!FileEnumInfo->PathPattern) {
|
|
DEBUGMSGA ((DBG_ERROR, "pGetFileEnumInfoA: bad EncodedPathPattern: %s", EncodedPathPattern));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// check for empty filename; no filename will match in this case
|
|
//
|
|
if (FileEnumInfo->PathPattern->Leaf && *FileEnumInfo->PathPattern->Leaf == 0) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"pGetFileEnumInfoA: empty filename pattern specified in EncodedPathPattern: %s",
|
|
EncodedPathPattern
|
|
));
|
|
ObsDestroyParsedPatternA (FileEnumInfo->PathPattern);
|
|
FileEnumInfo->PathPattern = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
if (FileEnumInfo->PathPattern->ExactRoot) {
|
|
if (!GetNodePatternMinMaxLevelsA (
|
|
FileEnumInfo->PathPattern->ExactRoot,
|
|
NULL,
|
|
&FileEnumInfo->RootLevel,
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
FileEnumInfo->RootLevel = 1;
|
|
}
|
|
|
|
if (!FileEnumInfo->PathPattern->LeafPattern) {
|
|
//
|
|
// no file pattern specified; assume only directory names will be returned
|
|
// overwrite caller's setting
|
|
//
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"pGetFileEnumInfoA: no filename pattern specified; forcing EnumDirs to TRUE"
|
|
));
|
|
EnumDirs = TRUE;
|
|
}
|
|
|
|
if (EnumDirs) {
|
|
FileEnumInfo->Flags |= FEIF_RETURN_DIRS;
|
|
}
|
|
if (ContainersFirst) {
|
|
FileEnumInfo->Flags |= FEIF_CONTAINERS_FIRST;
|
|
}
|
|
if (FilesFirst) {
|
|
FileEnumInfo->Flags |= FEIF_FILES_FIRST;
|
|
}
|
|
if (DepthFirst) {
|
|
FileEnumInfo->Flags |= FEIF_DEPTH_FIRST;
|
|
}
|
|
if (UseExclusions) {
|
|
FileEnumInfo->Flags |= FEIF_USE_EXCLUSIONS;
|
|
}
|
|
|
|
FileEnumInfo->MaxSubLevel = min (MaxSubLevel, FileEnumInfo->PathPattern->MaxSubLevel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pGetFileEnumInfoW (
|
|
OUT PFILEENUMINFOW FileEnumInfo,
|
|
IN PCWSTR EncodedPathPattern,
|
|
IN BOOL EnumDirs,
|
|
IN BOOL ContainersFirst,
|
|
IN BOOL FilesFirst,
|
|
IN BOOL DepthFirst,
|
|
IN DWORD MaxSubLevel,
|
|
IN BOOL UseExclusions
|
|
)
|
|
{
|
|
FileEnumInfo->PathPattern = ObsCreateParsedPatternW (EncodedPathPattern);
|
|
if (!FileEnumInfo->PathPattern) {
|
|
DEBUGMSGW ((DBG_ERROR, "pGetFileEnumInfoW: bad EncodedPathPattern: %s", EncodedPathPattern));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// check for empty filename; no filename will match in this case
|
|
//
|
|
if (FileEnumInfo->PathPattern->Leaf && *FileEnumInfo->PathPattern->Leaf == 0) {
|
|
DEBUGMSGW ((
|
|
DBG_ERROR,
|
|
"pGetFileEnumInfoW: empty filename pattern specified in EncodedPathPattern: %s",
|
|
EncodedPathPattern
|
|
));
|
|
ObsDestroyParsedPatternW (FileEnumInfo->PathPattern);
|
|
FileEnumInfo->PathPattern = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
if (FileEnumInfo->PathPattern->ExactRoot) {
|
|
if (!GetNodePatternMinMaxLevelsW (
|
|
FileEnumInfo->PathPattern->ExactRoot,
|
|
NULL,
|
|
&FileEnumInfo->RootLevel,
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
FileEnumInfo->RootLevel = 1;
|
|
}
|
|
|
|
if (!FileEnumInfo->PathPattern->LeafPattern) {
|
|
//
|
|
// no file pattern specified; assume only directory names will be returned
|
|
// overwrite caller's setting
|
|
//
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"pGetFileEnumInfoW: no filename pattern specified; forcing EnumDirs to TRUE"
|
|
));
|
|
EnumDirs = TRUE;
|
|
}
|
|
|
|
if (EnumDirs) {
|
|
FileEnumInfo->Flags |= FEIF_RETURN_DIRS;
|
|
}
|
|
if (ContainersFirst) {
|
|
FileEnumInfo->Flags |= FEIF_CONTAINERS_FIRST;
|
|
}
|
|
if (FilesFirst) {
|
|
FileEnumInfo->Flags |= FEIF_FILES_FIRST;
|
|
}
|
|
if (DepthFirst) {
|
|
FileEnumInfo->Flags |= FEIF_DEPTH_FIRST;
|
|
}
|
|
if (UseExclusions) {
|
|
FileEnumInfo->Flags |= FEIF_USE_EXCLUSIONS;
|
|
}
|
|
|
|
FileEnumInfo->MaxSubLevel = min (MaxSubLevel, FileEnumInfo->PathPattern->MaxSubLevel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGetCurrentDirNode returns the current dir node to be enumerated, based on DepthFirst flag
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the context
|
|
LastCreated - Specifies TRUE if the last created node is to be retrieved, regardless of
|
|
DepthFirst flag
|
|
|
|
Return Value:
|
|
|
|
The current node if any or NULL if none remaining.
|
|
|
|
--*/
|
|
|
|
PDIRNODEA
|
|
pGetCurrentDirNodeA (
|
|
IN PFILETREE_ENUMA FileEnum,
|
|
IN BOOL LastCreated
|
|
)
|
|
{
|
|
PGROWBUFFER gb = &FileEnum->FileNodes;
|
|
|
|
if (!gb->Buf || gb->End - gb->UserIndex < DWSIZEOF (DIRNODEA)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
|
|
return (PDIRNODEA)(gb->Buf + gb->End) - 1;
|
|
} else {
|
|
return (PDIRNODEA)(gb->Buf + gb->UserIndex);
|
|
}
|
|
}
|
|
|
|
PDIRNODEW
|
|
pGetCurrentDirNodeW (
|
|
IN PFILETREE_ENUMW FileEnum,
|
|
IN BOOL LastCreated
|
|
)
|
|
{
|
|
PGROWBUFFER gb = &FileEnum->FileNodes;
|
|
|
|
if (gb->End - gb->UserIndex < DWSIZEOF (DIRNODEW)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
|
|
return (PDIRNODEW)(gb->Buf + gb->End) - 1;
|
|
} else {
|
|
return (PDIRNODEW)(gb->Buf + gb->UserIndex);
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pDeleteDirNode frees the resources associated with the current dir node and destroys it
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the context
|
|
LastCreated - Specifies TRUE if the last created node is to be deleted, regardless of
|
|
DepthFirst flag
|
|
|
|
Return Value:
|
|
|
|
TRUE if there was a node to delete, FALSE if no more nodes
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pDeleteDirNodeA (
|
|
IN OUT PFILETREE_ENUMA FileEnum,
|
|
IN BOOL LastCreated
|
|
)
|
|
{
|
|
PDIRNODEA dirNode;
|
|
PGROWBUFFER gb = &FileEnum->FileNodes;
|
|
|
|
dirNode = pGetCurrentDirNodeA (FileEnum, LastCreated);
|
|
if (!dirNode) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (dirNode->DirName) {
|
|
FreeTextExA (g_FileEnumPool, dirNode->DirName);
|
|
}
|
|
|
|
if (dirNode->FindHandle) {
|
|
FindClose (dirNode->FindHandle);
|
|
dirNode->FindHandle = NULL;
|
|
}
|
|
|
|
if (FileEnum->LastNode == dirNode) {
|
|
FileEnum->LastNode = NULL;
|
|
}
|
|
|
|
//
|
|
// delete node
|
|
//
|
|
if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
|
|
gb->End -= DWSIZEOF (DIRNODEA);
|
|
} else {
|
|
gb->UserIndex += DWSIZEOF (DIRNODEA);
|
|
//
|
|
// shift list
|
|
//
|
|
if (gb->Size - gb->End < DWSIZEOF (DIRNODEA)) {
|
|
MoveMemory (gb->Buf, gb->Buf + gb->UserIndex, gb->End - gb->UserIndex);
|
|
gb->End -= gb->UserIndex;
|
|
gb->UserIndex = 0;
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pDeleteDirNodeW (
|
|
IN OUT PFILETREE_ENUMW FileEnum,
|
|
IN BOOL LastCreated
|
|
)
|
|
{
|
|
PDIRNODEW dirNode;
|
|
PGROWBUFFER gb = &FileEnum->FileNodes;
|
|
|
|
dirNode = pGetCurrentDirNodeW (FileEnum, LastCreated);
|
|
if (!dirNode) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (dirNode->DirName) {
|
|
FreeTextExW (g_FileEnumPool, dirNode->DirName);
|
|
}
|
|
|
|
if (dirNode->FindHandle) {
|
|
FindClose (dirNode->FindHandle);
|
|
dirNode->FindHandle = NULL;
|
|
}
|
|
|
|
if (FileEnum->LastNode == dirNode) {
|
|
FileEnum->LastNode = NULL;
|
|
}
|
|
|
|
//
|
|
// delete node
|
|
//
|
|
if (LastCreated || (FileEnum->FileEnumInfo.Flags & FEIF_DEPTH_FIRST)) {
|
|
gb->End -= DWSIZEOF (DIRNODEW);
|
|
} else {
|
|
gb->UserIndex += DWSIZEOF (DIRNODEW);
|
|
//
|
|
// shift list
|
|
//
|
|
if (gb->Size - gb->End < DWSIZEOF (DIRNODEW)) {
|
|
MoveMemory (gb->Buf, gb->Buf + gb->UserIndex, gb->End - gb->UserIndex);
|
|
gb->End -= gb->UserIndex;
|
|
gb->UserIndex = 0;
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pCreateDirNode creates a new node given a context, a dir name or a parent node
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the context
|
|
DirName - Specifies the dir name of the new node; may be NULL only if ParentNode is not NULL
|
|
ParentNode - Specifies a pointer to the parent node of the new node; a pointer to the node
|
|
is required because the parent node location in memory may change as a result
|
|
of the growbuffer changing buffer location when it grows;
|
|
may be NULL only if DirName is not;
|
|
Ignore - Receives a meaningful value only if NULL is returned (no node created);
|
|
if TRUE upon return, the failure of node creation should be ignored
|
|
|
|
Return Value:
|
|
|
|
A pointer to the new node or NULL if no node was created
|
|
|
|
--*/
|
|
|
|
PDIRNODEA
|
|
pCreateDirNodeA (
|
|
IN OUT PFILETREE_ENUMA FileEnum,
|
|
IN PCSTR DirName, OPTIONAL
|
|
IN PDIRNODEA* ParentNode, OPTIONAL
|
|
OUT PBOOL Ignore OPTIONAL
|
|
)
|
|
{
|
|
PDIRNODEA newNode;
|
|
PSTR newDirName;
|
|
PSEGMENTA FirstSegment;
|
|
LONG offset = 0;
|
|
|
|
if (DirName) {
|
|
newDirName = DuplicateTextExA (g_FileEnumPool, DirName, 0, NULL);
|
|
RemoveWackAtEndA (newDirName);
|
|
} else {
|
|
MYASSERT (ParentNode);
|
|
newDirName = JoinPathsInPoolExA ((
|
|
g_FileEnumPool,
|
|
(*ParentNode)->DirName,
|
|
(*ParentNode)->FindData.cFileName,
|
|
NULL
|
|
));
|
|
|
|
//
|
|
// check if this starting path may match the pattern before continuing
|
|
//
|
|
if (FileEnum->FileEnumInfo.PathPattern->NodePattern) {
|
|
FirstSegment = FileEnum->FileEnumInfo.PathPattern->NodePattern->Pattern->Segment;
|
|
} else {
|
|
FreeTextExA (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
if ((FirstSegment->Type == SEGMENTTYPE_EXACTMATCH) &&
|
|
(!StringIMatchByteCountA (
|
|
FirstSegment->Exact.LowerCasePhrase,
|
|
newDirName,
|
|
FirstSegment->Exact.PhraseBytes
|
|
))
|
|
) {
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"Skipping tree %s\\* because it cannot match the pattern",
|
|
newDirName
|
|
));
|
|
|
|
FreeTextExA (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// look if this dir and the whole subtree are excluded; if so, soft block creation of node
|
|
//
|
|
if (ElIsTreeExcluded2A (ELT_FILE, newDirName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
|
|
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"Skipping tree %s\\%s because it's excluded",
|
|
newDirName,
|
|
FileEnum->FileEnumInfo.PathPattern->Leaf
|
|
));
|
|
|
|
FreeTextExA (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (ParentNode) {
|
|
//
|
|
// remember current offset
|
|
//
|
|
offset = (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf);
|
|
}
|
|
//
|
|
// allocate space for the new node in the growbuffer
|
|
//
|
|
newNode = (PDIRNODEA) GbGrow (&FileEnum->FileNodes, DWSIZEOF (DIRNODEA));
|
|
if (!newNode) {
|
|
FreeTextExA (g_FileEnumPool, newDirName);
|
|
goto fail;
|
|
}
|
|
|
|
if (ParentNode) {
|
|
//
|
|
// check if the buffer moved
|
|
//
|
|
if (offset != (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf)) {
|
|
//
|
|
// adjust the parent position
|
|
//
|
|
*ParentNode = (PDIRNODEA)(FileEnum->FileNodes.Buf + offset);
|
|
}
|
|
}
|
|
|
|
//
|
|
// initialize the newly created node
|
|
//
|
|
ZeroMemory (newNode, DWSIZEOF (DIRNODEA));
|
|
|
|
newNode->DirName = newDirName;
|
|
|
|
if (DirName) {
|
|
newNode->DirAttributes = GetFileAttributesA (DirName);
|
|
//
|
|
// roots are not returned from enumeration because DNF_RETURN_DIRNAME is not set here
|
|
//
|
|
if ((FileEnum->FileEnumInfo.PathPattern->Leaf == NULL) &&
|
|
(FileEnum->FileEnumInfo.PathPattern->ExactRoot) &&
|
|
(!WildCharsPatternA (FileEnum->FileEnumInfo.PathPattern->NodePattern))
|
|
) {
|
|
newNode->Flags |= DNF_RETURN_DIRNAME;
|
|
}
|
|
} else {
|
|
MYASSERT (ParentNode);
|
|
//ParentNode is not NULL (see the assert above) so...
|
|
newNode->DirAttributes = (*ParentNode)->FindData.dwFileAttributes; //lint !e613
|
|
newNode->Flags |= DNF_RETURN_DIRNAME;
|
|
}
|
|
|
|
newNode->EnumState = DNS_ENUM_INIT;
|
|
|
|
if ((FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTNODE | OBSPF_NODEISROOTPLUSSTAR)) ||
|
|
TestParsedPatternA (FileEnum->FileEnumInfo.PathPattern->NodePattern, newDirName)
|
|
) {
|
|
newNode->Flags |= DNF_DIRNAME_MATCHES;
|
|
}
|
|
|
|
if (ParentNode) {
|
|
newNode->SubLevel = (*ParentNode)->SubLevel + 1;
|
|
} else {
|
|
newNode->SubLevel = 0;
|
|
}
|
|
|
|
return newNode;
|
|
|
|
fail:
|
|
if (Ignore) {
|
|
if (FileEnum->FileEnumInfo.CallbackOnError) {
|
|
*Ignore = (*FileEnum->FileEnumInfo.CallbackOnError)(newNode);
|
|
} else {
|
|
*Ignore = FALSE;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PDIRNODEW
|
|
pCreateDirNodeW (
|
|
IN OUT PFILETREE_ENUMW FileEnum,
|
|
IN PCWSTR DirName, OPTIONAL
|
|
IN PDIRNODEW* ParentNode, OPTIONAL
|
|
OUT PBOOL Ignore OPTIONAL
|
|
)
|
|
{
|
|
PDIRNODEW newNode;
|
|
PWSTR newDirName;
|
|
PSEGMENTW FirstSegment;
|
|
LONG offset = 0;
|
|
|
|
if (DirName) {
|
|
newDirName = DuplicateTextExW (g_FileEnumPool, DirName, 0, NULL);
|
|
RemoveWackAtEndW (newDirName);
|
|
} else {
|
|
MYASSERT (ParentNode);
|
|
newDirName = JoinPathsInPoolExW ((
|
|
g_FileEnumPool,
|
|
(*ParentNode)->DirName,
|
|
(*ParentNode)->FindData.cFileName,
|
|
NULL
|
|
));
|
|
|
|
//
|
|
// check if this starting path may match the pattern before continuing
|
|
//
|
|
if (FileEnum->FileEnumInfo.PathPattern->NodePattern) {
|
|
FirstSegment = FileEnum->FileEnumInfo.PathPattern->NodePattern->Pattern->Segment;
|
|
} else {
|
|
FreeTextExW (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
if ((FirstSegment->Type == SEGMENTTYPE_EXACTMATCH) &&
|
|
(!StringIMatchByteCountW (
|
|
FirstSegment->Exact.LowerCasePhrase,
|
|
newDirName,
|
|
FirstSegment->Exact.PhraseBytes
|
|
))
|
|
) { //lint !e64
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"Skipping tree %s\\* because it cannot match the pattern",
|
|
newDirName
|
|
));
|
|
|
|
FreeTextExW (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// look if this dir and the whole subtree are excluded; if so, soft block creation of node
|
|
//
|
|
if (ElIsTreeExcluded2W (ELT_FILE, newDirName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
|
|
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"Skipping tree %s\\%s because it's excluded",
|
|
newDirName,
|
|
FileEnum->FileEnumInfo.PathPattern->Leaf
|
|
));
|
|
|
|
FreeTextExW (g_FileEnumPool, newDirName);
|
|
|
|
if (Ignore) {
|
|
*Ignore = TRUE;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (ParentNode) {
|
|
//
|
|
// remember current offset
|
|
//
|
|
offset = (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf);
|
|
}
|
|
//
|
|
// allocate space for the new node in the growbuffer
|
|
//
|
|
newNode = (PDIRNODEW) GbGrow (&FileEnum->FileNodes, DWSIZEOF (DIRNODEW));
|
|
if (!newNode) {
|
|
FreeTextExW (g_FileEnumPool, newDirName);
|
|
goto fail;
|
|
}
|
|
|
|
if (ParentNode) {
|
|
//
|
|
// check if the buffer moved
|
|
//
|
|
if (offset != (LONG)((PBYTE)*ParentNode - FileEnum->FileNodes.Buf)) {
|
|
//
|
|
// adjust the parent position
|
|
//
|
|
*ParentNode = (PDIRNODEW)(FileEnum->FileNodes.Buf + offset);
|
|
}
|
|
}
|
|
|
|
//
|
|
// initialize the newly created node
|
|
//
|
|
ZeroMemory (newNode, DWSIZEOF (DIRNODEW));
|
|
|
|
newNode->DirName = newDirName;
|
|
|
|
if (DirName) {
|
|
newNode->DirAttributes = GetFileAttributesW (DirName);
|
|
//
|
|
// roots are not returned from enumeration because DNF_RETURN_DIRNAME is not set here
|
|
//
|
|
if ((FileEnum->FileEnumInfo.PathPattern->Leaf == NULL) &&
|
|
(FileEnum->FileEnumInfo.PathPattern->ExactRoot) &&
|
|
(!WildCharsPatternW (FileEnum->FileEnumInfo.PathPattern->NodePattern))
|
|
) {
|
|
newNode->Flags |= DNF_RETURN_DIRNAME;
|
|
}
|
|
} else {
|
|
MYASSERT (ParentNode);
|
|
//ParentNode is not NULL (see the assert above) so...
|
|
newNode->DirAttributes = (*ParentNode)->FindData.dwFileAttributes; //lint !e613
|
|
newNode->Flags |= DNF_RETURN_DIRNAME;
|
|
}
|
|
|
|
newNode->EnumState = DNS_ENUM_INIT;
|
|
|
|
if ((FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTNODE | OBSPF_NODEISROOTPLUSSTAR)) ||
|
|
TestParsedPatternW (FileEnum->FileEnumInfo.PathPattern->NodePattern, newDirName)
|
|
) {
|
|
newNode->Flags |= DNF_DIRNAME_MATCHES;
|
|
}
|
|
|
|
if (ParentNode) {
|
|
newNode->SubLevel = (*ParentNode)->SubLevel + 1;
|
|
} else {
|
|
newNode->SubLevel = 0;
|
|
}
|
|
|
|
return newNode;
|
|
|
|
fail:
|
|
if (Ignore) {
|
|
if (FileEnum->FileEnumInfo.CallbackOnError) {
|
|
*Ignore = (*FileEnum->FileEnumInfo.CallbackOnError)(newNode);
|
|
} else {
|
|
*Ignore = FALSE;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumNextFile enumerates the next file that matches caller's conditions
|
|
|
|
Arguments:
|
|
|
|
DirNode - Specifies the node and the current context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a new file was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumNextFileA (
|
|
IN OUT PDIRNODEA DirNode
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// ignore dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
break;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumNextFileW (
|
|
IN OUT PDIRNODEW DirNode
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// ignore dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
break;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumFirstFile enumerates the first file that matches caller's conditions
|
|
|
|
Arguments:
|
|
|
|
DirNode - Specifies the node and the current context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a first file was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumFirstFileA (
|
|
OUT PDIRNODEA DirNode,
|
|
IN PFILETREE_ENUMA FileEnum
|
|
)
|
|
{
|
|
PCSTR pattern;
|
|
PSEGMENTA FirstSegment;
|
|
PCSTR p;
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_EXACTLEAF) {
|
|
FirstSegment = FileEnum->FileEnumInfo.PathPattern->LeafPattern->Pattern->Segment;
|
|
p = FirstSegment->Exact.LowerCasePhrase;
|
|
MYASSERT (p && *p);
|
|
} else {
|
|
p = "*";
|
|
}
|
|
pattern = JoinPathsA (DirNode->DirName, p);
|
|
DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData);
|
|
FreePathStringA (pattern);
|
|
|
|
if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
//
|
|
// ignore dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
break;
|
|
}
|
|
|
|
if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumFirstFileW (
|
|
OUT PDIRNODEW DirNode,
|
|
IN PFILETREE_ENUMW FileEnum
|
|
)
|
|
{
|
|
PCWSTR pattern;
|
|
PSEGMENTW FirstSegment;
|
|
PCWSTR p;
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_EXACTLEAF) {
|
|
FirstSegment = FileEnum->FileEnumInfo.PathPattern->LeafPattern->Pattern->Segment;
|
|
p = FirstSegment->Exact.LowerCasePhrase;
|
|
MYASSERT (p && *p);
|
|
} else {
|
|
p = L"*";
|
|
}
|
|
|
|
pattern = JoinPathsW (DirNode->DirName, p);
|
|
DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData);
|
|
FreePathStringW (pattern);
|
|
|
|
if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
//
|
|
// ignore dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
break;
|
|
}
|
|
|
|
if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pIsSpecialDirName checks if the specified dir name is a special name (used by the OS)
|
|
|
|
Arguments:
|
|
|
|
DirName - Specifies the name
|
|
|
|
Return Value:
|
|
|
|
TRUE if it's a special dir name
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pIsSpecialDirNameA (
|
|
IN PCSTR DirName
|
|
)
|
|
{
|
|
return DirName[0] == '.' && (DirName[1] == 0 || (DirName[1] == '.' && DirName[2] == 0));
|
|
}
|
|
|
|
BOOL
|
|
pIsSpecialDirNameW (
|
|
IN PCWSTR DirName
|
|
)
|
|
{
|
|
return DirName[0] == L'.' && (DirName[1] == 0 || (DirName[1] == L'.' && DirName[2] == 0));
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumNextSubDir enumerates the next subdir that matches caller's conditions
|
|
|
|
Arguments:
|
|
|
|
DirNode - Specifies the node and the current context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a new subdir was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumNextSubDirA (
|
|
IN OUT PDIRNODEA DirNode
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// ignore special dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
continue;
|
|
}
|
|
if (!pIsSpecialDirNameA (DirNode->FindData.cFileName)) {
|
|
break;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumNextSubDirW (
|
|
IN OUT PDIRNODEW DirNode
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// ignore special dirs
|
|
//
|
|
if (!(DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
continue;
|
|
}
|
|
if (!pIsSpecialDirNameW (DirNode->FindData.cFileName)) {
|
|
break;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumFirstSubDir enumerates the first subdir that matches caller's conditions
|
|
|
|
Arguments:
|
|
|
|
DirNode - Specifies the node and the current context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a first subdir was found; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumFirstSubDirA (
|
|
OUT PDIRNODEA DirNode
|
|
)
|
|
{
|
|
PCSTR pattern;
|
|
|
|
pattern = JoinPathsA (DirNode->DirName, "*");
|
|
//
|
|
// NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExA)
|
|
//
|
|
DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData);
|
|
FreePathStringA (pattern);
|
|
|
|
if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
do {
|
|
//
|
|
// ignore special dirs
|
|
//
|
|
if ((DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
!pIsSpecialDirNameA (DirNode->FindData.cFileName)
|
|
) {
|
|
break;
|
|
}
|
|
|
|
if (!FindNextFileA (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumFirstSubDirW (
|
|
OUT PDIRNODEW DirNode
|
|
)
|
|
{
|
|
PCWSTR pattern;
|
|
|
|
pattern = JoinPathsW (DirNode->DirName, L"*");
|
|
//
|
|
// NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExW)
|
|
//
|
|
DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData);
|
|
FreePathStringW (pattern);
|
|
|
|
if (DirNode->FindHandle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
do {
|
|
//
|
|
// ignore special dirs
|
|
//
|
|
if ((DirNode->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
!pIsSpecialDirNameW (DirNode->FindData.cFileName)
|
|
) {
|
|
break;
|
|
}
|
|
|
|
if (!FindNextFileW (DirNode->FindHandle, &DirNode->FindData)) {
|
|
FindClose (DirNode->FindHandle);
|
|
DirNode->FindHandle = NULL;
|
|
return FALSE;
|
|
}
|
|
} while (TRUE); //lint !e506
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumNextFileInTree is a private function that enumerates the next node matching
|
|
the specified criteria; it's implemented as a state machine that travels the dirs/files
|
|
as specified the the caller; it doesn't check if they actually match the patterns
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the current enum context; receives updated info
|
|
CurrentDirNode - Receives the dir node that is currently processed, if success is returned
|
|
|
|
Return Value:
|
|
|
|
TRUE if a next match was found; FALSE if no more dirs/files match
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumNextFileInTreeA (
|
|
IN OUT PFILETREE_ENUMA FileEnum,
|
|
OUT PDIRNODEA* CurrentDirNode
|
|
)
|
|
{
|
|
PDIRNODEA currentNode;
|
|
PDIRNODEA newNode;
|
|
BOOL ignore;
|
|
|
|
while ((currentNode = pGetCurrentDirNodeA (FileEnum, FALSE)) != NULL) {
|
|
|
|
*CurrentDirNode = currentNode;
|
|
|
|
switch (currentNode->EnumState) {
|
|
|
|
case DNS_FILE_FIRST:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPFILES) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPFILES;
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumFirstFileA (currentNode, FileEnum)) {
|
|
currentNode->EnumState = DNS_FILE_NEXT;
|
|
return TRUE;
|
|
}
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
|
|
case DNS_FILE_NEXT:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPFILES) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPFILES;
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumNextFileA (currentNode)) {
|
|
return TRUE;
|
|
}
|
|
//
|
|
// no more files for this one, go to the next
|
|
//
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_FILE_DONE:
|
|
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
|
|
//
|
|
// done with this node
|
|
//
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
break;
|
|
}
|
|
//
|
|
// now enum subdirs
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_FIRST;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_FIRST:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// check current dir's level; if max level reached, don't recurse into subdirs
|
|
//
|
|
if (currentNode->SubLevel >= FileEnum->FileEnumInfo.MaxSubLevel) {
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
if (!pEnumFirstSubDirA (currentNode)) {
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
currentNode->EnumState = DNS_SUBDIR_NEXT;
|
|
newNode = pCreateDirNodeA (FileEnum, NULL, ¤tNode, &ignore);
|
|
if (newNode) {
|
|
//
|
|
// look at the new node first
|
|
//
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
newNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
*CurrentDirNode = newNode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (!ignore) {
|
|
//
|
|
// abort enum
|
|
//
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"Error encountered enumerating file system; aborting enumeration"
|
|
));
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_NEXT:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumNextSubDirA (currentNode)) {
|
|
newNode = pCreateDirNodeA (FileEnum, NULL, ¤tNode, &ignore);
|
|
if (newNode) {
|
|
//
|
|
// look at the new node first
|
|
//
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
newNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
*CurrentDirNode = newNode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// did it fail because of a soft block?
|
|
//
|
|
if (!ignore) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"Error encountered enumerating file system; aborting enumeration"
|
|
));
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// continue with next subdir
|
|
//
|
|
break;
|
|
}
|
|
//
|
|
// this node is done
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_DONE:
|
|
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
|
|
//
|
|
// now enum files
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
|
|
currentNode->EnumState = DNS_FILE_FIRST;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// done with this node
|
|
//
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_ENUM_DONE:
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST)) {
|
|
if (currentNode->Flags & DNF_RETURN_DIRNAME) {
|
|
currentNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
//
|
|
// before returning, set some data
|
|
//
|
|
currentNode->FindData.cFileName[0] = 0;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
pDeleteDirNodeA (FileEnum, FALSE);
|
|
break;
|
|
|
|
case DNS_ENUM_INIT:
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
if (currentNode->Flags & DNF_RETURN_DIRNAME) {
|
|
currentNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPDIR) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPDIR;
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
break;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST) {
|
|
//
|
|
// enum files
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
|
|
currentNode->EnumState = DNS_FILE_FIRST;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// enum subdirs
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_FIRST;
|
|
break;
|
|
|
|
default:
|
|
MYASSERT (FALSE); //lint !e506
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumNextFileInTreeW (
|
|
IN OUT PFILETREE_ENUMW FileEnum,
|
|
OUT PDIRNODEW* CurrentDirNode
|
|
)
|
|
{
|
|
PDIRNODEW currentNode;
|
|
PDIRNODEW newNode;
|
|
BOOL ignore;
|
|
|
|
while ((currentNode = pGetCurrentDirNodeW (FileEnum, FALSE)) != NULL) {
|
|
|
|
*CurrentDirNode = currentNode;
|
|
|
|
switch (currentNode->EnumState) {
|
|
|
|
case DNS_FILE_FIRST:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPFILES) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPFILES;
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumFirstFileW (currentNode, FileEnum)) {
|
|
currentNode->EnumState = DNS_FILE_NEXT;
|
|
return TRUE;
|
|
}
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
|
|
case DNS_FILE_NEXT:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPFILES) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPFILES;
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumNextFileW (currentNode)) {
|
|
return TRUE;
|
|
}
|
|
//
|
|
// no more files for this one, go to the next
|
|
//
|
|
currentNode->EnumState = DNS_FILE_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_FILE_DONE:
|
|
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
|
|
//
|
|
// done with this node
|
|
//
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
break;
|
|
}
|
|
//
|
|
// now enum subdirs
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_FIRST;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_FIRST:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// check current dir's level; if max level reached, don't recurse into subdirs
|
|
//
|
|
if (currentNode->SubLevel >= FileEnum->FileEnumInfo.MaxSubLevel) {
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
if (!pEnumFirstSubDirW (currentNode)) {
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
currentNode->EnumState = DNS_SUBDIR_NEXT;
|
|
newNode = pCreateDirNodeW (FileEnum, NULL, ¤tNode, &ignore);
|
|
if (newNode) {
|
|
//
|
|
// look at the new node first
|
|
//
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
newNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
*CurrentDirNode = newNode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// did it fail because of a soft block?
|
|
//
|
|
if (!ignore) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"Error encountered enumerating file system; aborting enumeration"
|
|
));
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_NEXT:
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPSUBDIRS) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPSUBDIRS;
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
break;
|
|
}
|
|
|
|
if (pEnumNextSubDirW (currentNode)) {
|
|
newNode = pCreateDirNodeW (FileEnum, NULL, ¤tNode, &ignore);
|
|
if (newNode) {
|
|
//
|
|
// look at the new node first
|
|
//
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
newNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
*CurrentDirNode = newNode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// did it fail because of a soft block?
|
|
//
|
|
if (!ignore) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"Error encountered enumerating file system; aborting enumeration"
|
|
));
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
return FALSE;
|
|
}
|
|
//
|
|
// continue with next subdir
|
|
//
|
|
break;
|
|
}
|
|
//
|
|
// this node is done
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_SUBDIR_DONE:
|
|
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST)) {
|
|
//
|
|
// now enum files
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
|
|
currentNode->EnumState = DNS_FILE_FIRST;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// done with this node
|
|
//
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
//
|
|
// fall through
|
|
//
|
|
case DNS_ENUM_DONE:
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (!(FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST)) {
|
|
if (currentNode->Flags & DNF_RETURN_DIRNAME) {
|
|
currentNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
//
|
|
// before returning, set some data
|
|
//
|
|
currentNode->FindData.cFileName[0] = 0;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
pDeleteDirNodeW (FileEnum, FALSE);
|
|
break;
|
|
|
|
case DNS_ENUM_INIT:
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_RETURN_DIRS) {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_CONTAINERS_FIRST) {
|
|
if (currentNode->Flags & DNF_RETURN_DIRNAME) {
|
|
currentNode->Flags &= ~DNF_RETURN_DIRNAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FileEnum->ControlFlags & FECF_SKIPDIR) {
|
|
FileEnum->ControlFlags &= ~FECF_SKIPDIR;
|
|
currentNode->EnumState = DNS_ENUM_DONE;
|
|
break;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_FILES_FIRST) {
|
|
//
|
|
// enum files
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & OBSPF_NOLEAF)) {
|
|
currentNode->EnumState = DNS_FILE_FIRST;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// enum subdirs
|
|
//
|
|
currentNode->EnumState = DNS_SUBDIR_FIRST;
|
|
break;
|
|
|
|
default:
|
|
MYASSERT (FALSE); //lint !e506
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumFirstFileRoot enumerates the first root that matches caller's conditions
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a root node was created; FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pEnumFirstFileRootA (
|
|
IN OUT PFILETREE_ENUMA FileEnum
|
|
)
|
|
{
|
|
PSTR root = NULL;
|
|
BOOL ignore;
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern->ExactRoot) {
|
|
root = pFileAllocateMemory (SizeOfStringA (FileEnum->FileEnumInfo.PathPattern->ExactRoot));
|
|
ObsDecodeStringA (root, FileEnum->FileEnumInfo.PathPattern->ExactRoot);
|
|
}
|
|
|
|
if (root) {
|
|
|
|
if (!BfPathIsDirectoryA (root)) {
|
|
DEBUGMSGA ((DBG_FILEENUM, "pEnumFirstFileRootA: Invalid root spec: %s", root));
|
|
pFileFreeMemory (root);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pCreateDirNodeA (FileEnum, root, NULL, NULL)) {
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
pFileFreeMemory (root);
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
FileEnum->DriveEnum = pFileAllocateMemory (DWSIZEOF (DRIVE_ENUMA));
|
|
|
|
if (!EnumFirstDriveA (FileEnum->DriveEnum, FileEnum->DriveEnumTypes)) {
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
if (ElIsTreeExcluded2A (ELT_FILE, FileEnum->DriveEnum->DriveName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
|
|
DEBUGMSGA ((DBG_FILEENUM, "pEnumFirstFileRootA: Root is excluded: %s", FileEnum->DriveEnum->DriveName));
|
|
continue;
|
|
}
|
|
}
|
|
if (!pCreateDirNodeA (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
|
|
if (ignore) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
FileEnum->RootState = FES_ROOT_NEXT;
|
|
return TRUE;
|
|
} while (EnumNextDriveA (FileEnum->DriveEnum));
|
|
|
|
pFileFreeMemory (FileEnum->DriveEnum);
|
|
FileEnum->DriveEnum = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumFirstFileRootW (
|
|
IN OUT PFILETREE_ENUMW FileEnum
|
|
)
|
|
{
|
|
PWSTR root = NULL;
|
|
BOOL ignore;
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern->ExactRoot) {
|
|
root = pFileAllocateMemory (SizeOfStringW (FileEnum->FileEnumInfo.PathPattern->ExactRoot));
|
|
ObsDecodeStringW (root, FileEnum->FileEnumInfo.PathPattern->ExactRoot);
|
|
}
|
|
|
|
if (root) {
|
|
|
|
if (!BfPathIsDirectoryW (root)) {
|
|
DEBUGMSGW ((DBG_FILEENUM, "pEnumFirstFileRootW: Invalid root spec: %s", root));
|
|
pFileFreeMemory (root);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pCreateDirNodeW (FileEnum, root, NULL, NULL)) {
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
pFileFreeMemory (root);
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
FileEnum->DriveEnum = pFileAllocateMemory (DWSIZEOF (DRIVE_ENUMA));
|
|
|
|
if (!EnumFirstDriveW (FileEnum->DriveEnum, FileEnum->DriveEnumTypes)) {
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
if (ElIsTreeExcluded2W (ELT_FILE, FileEnum->DriveEnum->DriveName, FileEnum->FileEnumInfo.PathPattern->Leaf)) {
|
|
DEBUGMSGW ((DBG_FILEENUM, "pEnumFirstFileRootW: Root is excluded: %s", FileEnum->DriveEnum->DriveName));
|
|
continue;
|
|
}
|
|
}
|
|
if (!pCreateDirNodeW (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
|
|
if (ignore) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
FileEnum->RootState = FES_ROOT_NEXT;
|
|
return TRUE;
|
|
} while (EnumNextDriveW (FileEnum->DriveEnum));
|
|
|
|
pFileFreeMemory (FileEnum->DriveEnum);
|
|
FileEnum->DriveEnum = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pEnumNextFileRootA (
|
|
IN OUT PFILETREE_ENUMA FileEnum
|
|
)
|
|
{
|
|
BOOL ignore;
|
|
|
|
while (EnumNextDriveA (FileEnum->DriveEnum)) {
|
|
if (pCreateDirNodeA (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
|
|
return TRUE;
|
|
}
|
|
if (!ignore) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
pEnumNextFileRootW (
|
|
IN OUT PFILETREE_ENUMW FileEnum
|
|
)
|
|
{
|
|
BOOL ignore;
|
|
|
|
while (EnumNextDriveW (FileEnum->DriveEnum)) {
|
|
if (pCreateDirNodeW (FileEnum, FileEnum->DriveEnum->DriveName, NULL, &ignore)) {
|
|
return TRUE;
|
|
}
|
|
if (!ignore) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
FileEnum->RootState = FES_ROOT_DONE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumFirstFileInTreeEx enumerates file system dirs, and optionally files, that match the
|
|
specified criteria
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Receives the enum context info; this will be used in subsequent calls to
|
|
EnumNextFileInTree
|
|
EncodedPathPattern - Specifies the encoded dir pattern (encoded as defined by the
|
|
ParsedPattern functions)
|
|
EncodedFilePattern - Specifies the encoded file pattern (encoded as defined by the
|
|
ParsedPattern functions); optional; NULL means no files
|
|
should be returned (only look for dirs)
|
|
EnumDirs - Specifies TRUE if directories should be returned during the enumeration
|
|
(if they match the pattern)
|
|
ContainersFirst - Specifies TRUE if directories should be returned before any of its
|
|
files or subdirs
|
|
FilesFirst - Specifies TRUE if a dir's files should be returned before dir's subdirs;
|
|
this parameter decides the enum order between files and subdirs
|
|
for each dir
|
|
DepthFirst - Specifies TRUE if the current subdir of any dir should be fully enumerated
|
|
before going to the next subdir; this parameter decides if the tree
|
|
traversal is depth-first (TRUE) or width-first (FALSE)
|
|
MaxSubLevel - Specifies the maximum sub-level of a subdir that is to be enumerated,
|
|
relative to the root; if 0, only the root is enumerated;
|
|
if -1, all sub-levels are enumerated
|
|
UseExclusions - Specifies TRUE if exclusion APIs should be used to determine if certain
|
|
paths/files are excluded from enumeration; this slows down the speed
|
|
CallbackOnError - Specifies a pointer to a callback function that will be called during
|
|
enumeration if an error occurs; if the callback is defined and it
|
|
returns FALSE, the enumeration is aborted, otherwise it will continue
|
|
ignoring the error
|
|
|
|
Return Value:
|
|
|
|
TRUE if a first match is found.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
EnumFirstFileInTreeExA (
|
|
OUT PFILETREE_ENUMA FileEnum,
|
|
IN PCSTR EncodedPathPattern,
|
|
IN UINT DriveEnumTypes,
|
|
IN BOOL EnumDirs,
|
|
IN BOOL ContainersFirst,
|
|
IN BOOL FilesFirst,
|
|
IN BOOL DepthFirst,
|
|
IN DWORD MaxSubLevel,
|
|
IN BOOL UseExclusions,
|
|
IN FPE_ERROR_CALLBACKA CallbackOnError OPTIONAL
|
|
)
|
|
{
|
|
MYASSERT (FileEnum && EncodedPathPattern && *EncodedPathPattern);
|
|
MYASSERT (g_FileEnumPool);
|
|
|
|
ZeroMemory (FileEnum, DWSIZEOF (*FileEnum)); //lint !e613 !e668
|
|
|
|
FileEnum->DriveEnumTypes = DriveEnumTypes;
|
|
|
|
//
|
|
// first try to get dir enum info in internal format
|
|
//
|
|
if (!pGetFileEnumInfoA (
|
|
/*lint -e(613)*/&FileEnum->FileEnumInfo,
|
|
EncodedPathPattern,
|
|
EnumDirs,
|
|
ContainersFirst,
|
|
FilesFirst,
|
|
DepthFirst,
|
|
MaxSubLevel,
|
|
UseExclusions
|
|
)) {
|
|
AbortEnumFileInTreeA (FileEnum);
|
|
return FALSE;
|
|
}
|
|
if (UseExclusions) {
|
|
//
|
|
// next check if the starting key is in an excluded tree
|
|
//
|
|
if (ElIsObsPatternExcludedA (ELT_FILE, /*lint -e(613)*/FileEnum->FileEnumInfo.PathPattern)) {
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"EnumFirstFileInTreeExA: Root is excluded: %s",
|
|
EncodedPathPattern
|
|
));
|
|
AbortEnumFileInTreeA (FileEnum);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!pEnumFirstFileRootA (FileEnum)) {
|
|
AbortEnumFileInTreeA (FileEnum);
|
|
return FALSE;
|
|
}
|
|
|
|
/*lint -e(613)*/FileEnum->FileEnumInfo.CallbackOnError = CallbackOnError;
|
|
|
|
return EnumNextFileInTreeA (FileEnum);
|
|
}
|
|
|
|
BOOL
|
|
EnumFirstFileInTreeExW (
|
|
OUT PFILETREE_ENUMW FileEnum,
|
|
IN PCWSTR EncodedPathPattern,
|
|
IN UINT DriveEnumTypes,
|
|
IN BOOL EnumDirs,
|
|
IN BOOL ContainersFirst,
|
|
IN BOOL FilesFirst,
|
|
IN BOOL DepthFirst,
|
|
IN DWORD MaxSubLevel,
|
|
IN BOOL UseExclusions,
|
|
IN FPE_ERROR_CALLBACKW CallbackOnError OPTIONAL
|
|
)
|
|
{
|
|
MYASSERT (FileEnum && EncodedPathPattern && *EncodedPathPattern);
|
|
MYASSERT (g_FileEnumPool);
|
|
|
|
ZeroMemory (FileEnum, DWSIZEOF (*FileEnum)); //lint !e613 !e668
|
|
|
|
FileEnum->DriveEnumTypes = DriveEnumTypes;
|
|
|
|
//
|
|
// first try to get dir enum info in internal format
|
|
//
|
|
if (!pGetFileEnumInfoW (
|
|
/*lint -e(613)*/&FileEnum->FileEnumInfo,
|
|
EncodedPathPattern,
|
|
EnumDirs,
|
|
ContainersFirst,
|
|
FilesFirst,
|
|
DepthFirst,
|
|
MaxSubLevel,
|
|
UseExclusions
|
|
)) {
|
|
AbortEnumFileInTreeW (FileEnum);
|
|
return FALSE;
|
|
}
|
|
if (UseExclusions) {
|
|
//
|
|
// next check if the starting key is in an excluded tree
|
|
//
|
|
if (ElIsObsPatternExcludedW (ELT_FILE, /*lint -e(613)*/FileEnum->FileEnumInfo.PathPattern)) {
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"EnumFirstFileInTreeExW: Root is excluded: %s",
|
|
EncodedPathPattern
|
|
));
|
|
AbortEnumFileInTreeW (FileEnum);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!pEnumFirstFileRootW (FileEnum)) {
|
|
AbortEnumFileInTreeW (FileEnum);
|
|
return FALSE;
|
|
}
|
|
|
|
/*lint -e(613)*/FileEnum->FileEnumInfo.CallbackOnError = CallbackOnError;
|
|
|
|
return EnumNextFileInTreeW (FileEnum);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pTestLeafPatternA (
|
|
IN PPARSEDPATTERNA ParsedPattern,
|
|
IN PCSTR LeafToTest
|
|
)
|
|
{
|
|
PSTR newLeaf;
|
|
BOOL result = TRUE;
|
|
|
|
if (!TestParsedPatternA (ParsedPattern, LeafToTest)) {
|
|
newLeaf = JoinTextA (LeafToTest, ".");
|
|
result = TestParsedPatternA (ParsedPattern, newLeaf);
|
|
FreeTextA (newLeaf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
pTestLeafPatternW (
|
|
IN PPARSEDPATTERNW ParsedPattern,
|
|
IN PCWSTR LeafToTest
|
|
)
|
|
{
|
|
PWSTR newLeaf;
|
|
BOOL result = TRUE;
|
|
|
|
if (!TestParsedPatternW (ParsedPattern, LeafToTest)) {
|
|
newLeaf = JoinTextW (LeafToTest, L".");
|
|
result = TestParsedPatternW (ParsedPattern, newLeaf);
|
|
FreeTextW (newLeaf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumNextFileInTree enumerates the next node matching the criteria specified in
|
|
FileEnum; this is filled on the call to EnumFirstFileInTreeEx;
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the current enum context; receives updated info
|
|
|
|
Return Value:
|
|
|
|
TRUE if a next match was found; FALSE if no more dirs/files match
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
EnumNextFileInTreeA (
|
|
IN OUT PFILETREE_ENUMA FileEnum
|
|
)
|
|
{
|
|
PDIRNODEA currentNode;
|
|
BOOL success;
|
|
|
|
MYASSERT (FileEnum);
|
|
|
|
do {
|
|
if (FileEnum->EncodedFullName) {
|
|
ObsFreeA (FileEnum->EncodedFullName);
|
|
FileEnum->EncodedFullName = NULL;
|
|
}
|
|
|
|
while (TRUE) { //lint !e506
|
|
|
|
if (FileEnum->LastWackPtr) {
|
|
*FileEnum->LastWackPtr = '\\';
|
|
FileEnum->LastWackPtr = NULL;
|
|
}
|
|
|
|
if (!pEnumNextFileInTreeA (FileEnum, ¤tNode)) {
|
|
break;
|
|
}
|
|
|
|
MYASSERT (currentNode && currentNode->DirName);
|
|
|
|
//
|
|
// check if this object matches the pattern
|
|
//
|
|
if (!(currentNode->Flags & DNF_DIRNAME_MATCHES)) { //lint !e613
|
|
continue;
|
|
}
|
|
|
|
if (/*lint -e(613)*/currentNode->FindData.cFileName[0] == 0) {
|
|
MYASSERT (/*lint -e(613)*/currentNode->DirAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
|
|
FileEnum->LastWackPtr = _mbsrchr (FileEnum->Location, '\\');
|
|
if (!FileEnum->LastWackPtr) {
|
|
FileEnum->Name = FileEnum->Location;
|
|
} else {
|
|
FileEnum->Name = _mbsinc (FileEnum->LastWackPtr);
|
|
if (!FileEnum->Name) {
|
|
FileEnum->Name = FileEnum->Location;
|
|
}
|
|
}
|
|
|
|
//
|
|
// prepare full path buffer
|
|
//
|
|
if (SizeOfStringA (FileEnum->Location) / DWSIZEOF(CHAR)> DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(CHAR)) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"Directory %s was found, but its path is too long",
|
|
FileEnum->Location
|
|
));
|
|
continue;
|
|
}
|
|
StringCopyA (FileEnum->NativeFullName, FileEnum->Location);
|
|
FileEnum->LastNode = currentNode;
|
|
FileEnum->FileNameAppendPos = NULL;
|
|
|
|
FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes;
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// check if this object is excluded
|
|
//
|
|
if (ElIsExcluded2A (ELT_FILE, FileEnum->Location, NULL)) {
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"Object %s was found, but it's excluded",
|
|
FileEnum->NativeFullName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExA (
|
|
FileEnum->Location,
|
|
NULL,
|
|
TRUE
|
|
);
|
|
} else {
|
|
|
|
FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
|
|
FileEnum->Name = /*lint -e(613)*/currentNode->FindData.cFileName;
|
|
|
|
//
|
|
// test if the filename matches
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTLEAF | OBSPF_OPTIONALLEAF)) &&
|
|
!pTestLeafPatternA (
|
|
FileEnum->FileEnumInfo.PathPattern->LeafPattern,
|
|
/*lint -e(613)*/currentNode->FindData.cFileName
|
|
)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
if (ElIsExcluded2A (ELT_FILE, NULL, /*lint -e(613)*/currentNode->FindData.cFileName)) {
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"File %s\\%s was found, but it's excluded by filename",
|
|
FileEnum->Location,
|
|
/*lint -e(613)*/currentNode->FindData.cFileName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (FileEnum->LastNode != currentNode) {
|
|
FileEnum->LastNode = currentNode;
|
|
//
|
|
// prepare full path buffer
|
|
//
|
|
FileEnum->NativeFullName[0] = 0;
|
|
FileEnum->FileNameAppendPos = StringCatA (FileEnum->NativeFullName, FileEnum->Location);
|
|
if (FileEnum->FileNameAppendPos) {
|
|
*FileEnum->FileNameAppendPos++ = '\\';
|
|
}
|
|
} else if (!FileEnum->FileNameAppendPos) {
|
|
FileEnum->FileNameAppendPos = GetEndOfStringA (FileEnum->NativeFullName);
|
|
if (FileEnum->FileNameAppendPos) {
|
|
*FileEnum->FileNameAppendPos++ = '\\';
|
|
}
|
|
}
|
|
|
|
if (FileEnum->FileNameAppendPos + SizeOfStringA (FileEnum->Name) / DWSIZEOF(CHAR)>
|
|
FileEnum->NativeFullName + DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(CHAR)) {
|
|
DEBUGMSGA ((
|
|
DBG_ERROR,
|
|
"File %s\\%s was found, but its path is too long",
|
|
FileEnum->Location,
|
|
FileEnum->Name
|
|
));
|
|
continue;
|
|
}
|
|
|
|
StringCopyA (FileEnum->FileNameAppendPos, FileEnum->Name);
|
|
FileEnum->Attributes = /*lint -e(613)*/currentNode->FindData.dwFileAttributes;
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// check if this object is excluded
|
|
//
|
|
if (ElIsExcluded2A (ELT_FILE, FileEnum->Location, FileEnum->Name)) {
|
|
DEBUGMSGA ((
|
|
DBG_FILEENUM,
|
|
"Object %s was found, but it's excluded",
|
|
FileEnum->NativeFullName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExA (
|
|
FileEnum->Location,
|
|
FileEnum->Name,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (FileEnum->LastWackPtr) {
|
|
*FileEnum->LastWackPtr = 0;
|
|
}
|
|
|
|
FileEnum->CurrentLevel = FileEnum->FileEnumInfo.RootLevel + /*lint -e(613)*/currentNode->SubLevel;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// try the next root
|
|
//
|
|
if (FileEnum->RootState == FES_ROOT_DONE) {
|
|
break;
|
|
}
|
|
|
|
MYASSERT (FileEnum->RootState == FES_ROOT_NEXT);
|
|
MYASSERT (FileEnum->DriveEnum);
|
|
success = pEnumNextFileRootA (FileEnum);
|
|
|
|
} while (success);
|
|
|
|
AbortEnumFileInTreeA (FileEnum);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
EnumNextFileInTreeW (
|
|
IN OUT PFILETREE_ENUMW FileEnum
|
|
)
|
|
{
|
|
PDIRNODEW currentNode;
|
|
BOOL success;
|
|
|
|
MYASSERT (FileEnum);
|
|
|
|
do {
|
|
if (FileEnum->EncodedFullName) {
|
|
ObsFreeW (FileEnum->EncodedFullName);
|
|
FileEnum->EncodedFullName = NULL;
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
if (FileEnum->LastWackPtr) {
|
|
*FileEnum->LastWackPtr = L'\\';
|
|
FileEnum->LastWackPtr = NULL;
|
|
}
|
|
|
|
if (!pEnumNextFileInTreeW (FileEnum, ¤tNode)) {
|
|
break;
|
|
}
|
|
|
|
MYASSERT (currentNode && currentNode->DirName);
|
|
|
|
//
|
|
// check if this object matches the pattern
|
|
//
|
|
if (!(currentNode->Flags & DNF_DIRNAME_MATCHES)) { //lint !e613
|
|
continue;
|
|
}
|
|
|
|
if (/*lint -e(613)*/currentNode->FindData.cFileName[0] == 0) {
|
|
MYASSERT (/*lint -e(613)*/currentNode->DirAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
|
|
FileEnum->LastWackPtr = wcsrchr (FileEnum->Location, L'\\');
|
|
if (!FileEnum->LastWackPtr) {
|
|
FileEnum->Name = FileEnum->Location;
|
|
} else {
|
|
FileEnum->Name = FileEnum->LastWackPtr + 1;
|
|
if (!FileEnum->Name) {
|
|
FileEnum->Name = FileEnum->Location;
|
|
}
|
|
}
|
|
|
|
//
|
|
// prepare full path buffer
|
|
//
|
|
if (SizeOfStringW (FileEnum->Location) / DWSIZEOF(WCHAR)> DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(WCHAR)) {
|
|
DEBUGMSGW ((
|
|
DBG_ERROR,
|
|
"Directory %s was found, but its path is too long",
|
|
FileEnum->Location
|
|
));
|
|
continue;
|
|
}
|
|
StringCopyW (FileEnum->NativeFullName, FileEnum->Location);
|
|
FileEnum->LastNode = currentNode;
|
|
FileEnum->FileNameAppendPos = NULL;
|
|
|
|
FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes;
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// check if this object is excluded
|
|
//
|
|
if (ElIsExcluded2W (ELT_FILE, FileEnum->Location, NULL)) {
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"Object %s was found, but it's excluded",
|
|
FileEnum->NativeFullName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExW (
|
|
FileEnum->Location,
|
|
NULL,
|
|
TRUE
|
|
);
|
|
} else {
|
|
|
|
FileEnum->Location = /*lint -e(613)*/currentNode->DirName;
|
|
FileEnum->Name = /*lint -e(613)*/currentNode->FindData.cFileName;
|
|
|
|
//
|
|
// test if the filename matches
|
|
//
|
|
if (!(FileEnum->FileEnumInfo.PathPattern->Flags & (OBSPF_EXACTLEAF | OBSPF_OPTIONALLEAF)) &&
|
|
!pTestLeafPatternW (
|
|
FileEnum->FileEnumInfo.PathPattern->LeafPattern,
|
|
/*lint -e(613)*/currentNode->FindData.cFileName
|
|
)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
if (ElIsExcluded2W (ELT_FILE, NULL, /*lint -e(613)*/currentNode->FindData.cFileName)) {
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"File %s\\%s was found, but it's excluded by filename",
|
|
FileEnum->Location,
|
|
/*lint -e(613)*/currentNode->FindData.cFileName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (FileEnum->LastNode != currentNode) {
|
|
FileEnum->LastNode = currentNode;
|
|
//
|
|
// prepare full path buffer
|
|
//
|
|
FileEnum->NativeFullName[0] = 0;
|
|
FileEnum->FileNameAppendPos = StringCatW (FileEnum->NativeFullName, FileEnum->Location);
|
|
if (FileEnum->FileNameAppendPos) {
|
|
*FileEnum->FileNameAppendPos++ = L'\\';
|
|
}
|
|
} else if (!FileEnum->FileNameAppendPos) {
|
|
FileEnum->FileNameAppendPos = GetEndOfStringW (FileEnum->NativeFullName);
|
|
if (FileEnum->FileNameAppendPos) {
|
|
*FileEnum->FileNameAppendPos++ = L'\\';
|
|
}
|
|
}
|
|
MYASSERT (FileEnum->Name && *FileEnum->Name);
|
|
|
|
if (FileEnum->FileNameAppendPos + SizeOfStringW (FileEnum->Name) / DWSIZEOF(WCHAR)>
|
|
FileEnum->NativeFullName + DWSIZEOF (FileEnum->NativeFullName) / DWSIZEOF(WCHAR)) {
|
|
DEBUGMSGW ((
|
|
DBG_ERROR,
|
|
"File %s\\%s was found, but its path is too long",
|
|
FileEnum->Location,
|
|
FileEnum->Name
|
|
));
|
|
continue;
|
|
}
|
|
|
|
StringCopyW (FileEnum->FileNameAppendPos, FileEnum->Name);
|
|
FileEnum->Attributes = /*lint -e(613)*/currentNode->FindData.dwFileAttributes;
|
|
|
|
if (FileEnum->FileEnumInfo.Flags & FEIF_USE_EXCLUSIONS) {
|
|
//
|
|
// check if this object is excluded
|
|
//
|
|
if (ElIsExcluded2W (ELT_FILE, FileEnum->Location, FileEnum->Name)) {
|
|
DEBUGMSGW ((
|
|
DBG_FILEENUM,
|
|
"Object %s was found, but it's excluded",
|
|
FileEnum->NativeFullName
|
|
));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
FileEnum->EncodedFullName = ObsBuildEncodedObjectStringExW (
|
|
FileEnum->Location,
|
|
FileEnum->Name,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (FileEnum->LastWackPtr) {
|
|
*FileEnum->LastWackPtr = 0;
|
|
}
|
|
|
|
FileEnum->CurrentLevel = FileEnum->FileEnumInfo.RootLevel + /*lint -e(613)*/currentNode->SubLevel;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// try the next root
|
|
//
|
|
if (FileEnum->RootState == FES_ROOT_DONE) {
|
|
break;
|
|
}
|
|
|
|
MYASSERT (FileEnum->RootState == FES_ROOT_NEXT);
|
|
MYASSERT (FileEnum->DriveEnum);
|
|
success = pEnumNextFileRootW (FileEnum);
|
|
|
|
} while (success);
|
|
|
|
AbortEnumFileInTreeW (FileEnum);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AbortEnumFileInTree aborts the enumeration, freeing all resources allocated
|
|
|
|
Arguments:
|
|
|
|
FileEnum - Specifies the current enum context; receives a "clean" context
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
VOID
|
|
AbortEnumFileInTreeA (
|
|
IN OUT PFILETREE_ENUMA FileEnum
|
|
)
|
|
{
|
|
while (pDeleteDirNodeA (FileEnum, TRUE)) {
|
|
}
|
|
GbFree (&FileEnum->FileNodes);
|
|
|
|
if (FileEnum->EncodedFullName) {
|
|
ObsFreeA (FileEnum->EncodedFullName);
|
|
FileEnum->EncodedFullName = NULL;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern) {
|
|
ObsDestroyParsedPatternA (FileEnum->FileEnumInfo.PathPattern);
|
|
FileEnum->FileEnumInfo.PathPattern = NULL;
|
|
}
|
|
|
|
if (FileEnum->DriveEnum) {
|
|
pFileFreeMemory (FileEnum->DriveEnum);
|
|
FileEnum->DriveEnum = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AbortEnumFileInTreeW (
|
|
IN OUT PFILETREE_ENUMW FileEnum
|
|
)
|
|
{
|
|
while (pDeleteDirNodeW (FileEnum, TRUE)) {
|
|
}
|
|
GbFree (&FileEnum->FileNodes);
|
|
|
|
if (FileEnum->EncodedFullName) {
|
|
ObsFreeW (FileEnum->EncodedFullName);
|
|
FileEnum->EncodedFullName = NULL;
|
|
}
|
|
|
|
if (FileEnum->FileEnumInfo.PathPattern) {
|
|
ObsDestroyParsedPatternW (FileEnum->FileEnumInfo.PathPattern);
|
|
FileEnum->FileEnumInfo.PathPattern = NULL;
|
|
}
|
|
|
|
if (FileEnum->DriveEnum) {
|
|
pFileFreeMemory (FileEnum->DriveEnum);
|
|
FileEnum->DriveEnum = NULL;
|
|
}
|
|
}
|
|
|
|
|