|
|
/*++
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 ) { CHAR pattern[MAX_MBCHAR_PATH]; 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 = "*"; } StringCopyA (pattern, DirNode->DirName); StringCopyA (AppendWackA (pattern), p);
DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData); 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 ) { WCHAR pattern[MAX_WCHAR_PATH]; 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"*"; }
StringCopyW (pattern, DirNode->DirName); StringCopyW (AppendWackW (pattern), p);
DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData); 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 ) { CHAR pattern[MAX_MBCHAR_PATH];
StringCopyA (pattern, DirNode->DirName); StringCopyA (AppendWackA (pattern), "*");
//
// NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExA)
//
DirNode->FindHandle = FindFirstFileA (pattern, &DirNode->FindData); 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 ) { WCHAR pattern[MAX_WCHAR_PATH];
StringCopyW (pattern, DirNode->DirName); StringCopyW (AppendWackW (pattern), L"*");
//
// NTRAID#NTBUG9-153302-2000/08/01-jimschm this should be enhanced for NT (it supports FindFirstFileExW)
//
DirNode->FindHandle = FindFirstFileW (pattern, &DirNode->FindData); 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; } } FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes; //
// prepare full path buffer
//
StringCopyA (FileEnum->NativeFullName, FileEnum->Location); FileEnum->LastNode = currentNode; FileEnum->FileNameAppendPos = NULL;
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 it's 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; } } FileEnum->Attributes = /*lint -e(613)*/currentNode->DirAttributes; //
// prepare full path buffer
//
StringCopyW (FileEnum->NativeFullName, FileEnum->Location); FileEnum->LastNode = currentNode; FileEnum->FileNameAppendPos = NULL;
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 it's 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; } }
|