uLib  User mode C/C++ extended API library for Win32 programmers.
FileDir.cpp
Go to the documentation of this file.
1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 // Project: uLib - User mode library.
3 // Module: Comprehensive file directory tree using DLinkList.
4 // Author: Copyright (c) Love Nystrom
5 // License: NNOSL (BSD descendant, see NNOSL.txt in the base directory).
6 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 
8 #include <uLib/FileDir.h>
9 #include <uLib/UtilFunc.h>
10 #include <uLib/StrFunc.h>
11 #include <uLib/LargeInt.h>
12 #include <uLib/Debug.h>
13 
14 #define FILEDIR_VER 3
15 
16 static CCSTR ANY_FNAME = _T("*.*");
17 ScanDirCtx _defScanCtx; // Default scanning context if none provided.
18 
20 {
21  MaxNest = Depth;
22  NestDepth = 0;
24  UserData = NULL;
25  ErrorDirs = NULL;
26  memset( CurrentDir, 0, sizeof(CurrentDir) );
28 }
29 
31 {
32  if (ErrorDirs)
33  {
34  ErrorDirs->RemoveAll( StringEntry::_delete );
35  delete ErrorDirs;
36  }
37 }
38 
39 //==---------------------------------------------------------------------------
40 
41 PFileEntry __stdcall __new_FileEntry( LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx ) {
42  return new FileEntry( pfd, pDir, pCtx );
43  }
44 PDirEntry __stdcall __new_DirEntry( LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx ) {
45  return new DirEntry( pfd, pDir, pCtx );
46  }
47 
48 // Two function pointers to allow DirEntry/FileEntry polymorphism during Scan().
49 
50 PFileEntry (__stdcall *NewFileEntry)( LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx )
52 
53 PDirEntry (__stdcall *NewDirEntry)( LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx )
55 
56 // DLinkList::QuickSort callback to sort directories alphabetically.
57 
58 static int __stdcall __sort_DirEntry( PLIST_ENTRY X, PLIST_ENTRY Y, void* pData )
59 {
60  PDirEntry pItem1 = (PDirEntry)X, pItem2 = (PDirEntry)Y;
61  int rc = 0;
62  if (pItem1 && pItem2) { // Case insensitive alphabetical sort
63  rc = _tcsicmp( pItem1->Name, pItem2->Name );
64  } else { // Handle the error gracefully. (This case has *never* occured.)
65  if (pItem1) rc = -1; else if (pItem2) rc = 1;
66  }
67  return rc;
68 }
69 
70 //==---------------------------------------------------------------------------
71 #if _DEBUG // Debugging utility callbacks
72 //==---------------------------------------------------------------------------
73 
74 bool __stdcall FileEntry::_dump( PLIST_ENTRY Entry, void* pData )
75 {
76  PFileEntry pFile = (PFileEntry) Entry;
77  if (pFile) {
78  #if 0 // Show full file pathname
79  TCHAR pathname[ MAX_PATH ];
80  pFile->GetPathName( pathname, MAX_PATH );
81  DPrint( DP_DEBUG, _T(" File: %s\r\n"), pathname );
82  #else
83  DPrint( DP_DEBUG, _T(" File: %s\r\n"), pFile->Name );
84  #endif
85  }
86  return true;
87 }
88 
89 bool __stdcall DirEntry::_dump( PLIST_ENTRY Entry, void* pData )
90 {
91  PDirEntry pDir = (PDirEntry) Entry;
92  if (pDir) {
93  #if 1 // Show full directory path
94  TCHAR path[ MAX_PATH ];
95  pDir->GetFullPath( path, MAX_PATH );
96  DPrint( DP_DEBUG, _T("Dir: %s\r\n"), path );
97  #else
98  DPrint( DP_DEBUG, _T("Dir: %s\r\n"), pDir->Name );
99  #endif
100  if (pDir->SubDir) pDir->SubDir->ForEach( DirEntry::_dump );
101  if (pDir->Files) pDir->Files->ForEach( FileEntry::_dump );
102  }
103  return true;
104 }
105 #endif //_DEBUG
106 
107 //==---------------------------------------------------------------------------
108 // FileEntry
109 //==---------------------------------------------------------------------------
110 
111 PFnFileAddAction FileEntry::AddAction = NULL; // static
112 PFnFileAction FileEntry::DelAction = NULL; // static
113 
114 void FileEntry::_init( CSTR name, PDirEntry dir )
115 {
116  //Flink = Blink = PLIST_ENTRY(&Flink);
117  InitializeListEntry( this ); // Flink = Blink = this;
118  //
119  // Note: (this) != (&Flink), since FileEntry has a virtual destructor,
120  // which means it must have a v-table, and (this) points to *it* rather
121  // than the first data member, which is an unfortunate design choice
122  // in the compiler. It would have been better to put the v-table
123  // pointer at (this-sizeof(ptr)) so that (this) always pointed to
124  // the member data, whether the class had a v-table or not.
125  //
126  pData = NULL;
127  pDir = dir; // Back pointer to containing directory
128  Rename( name );
129  #if USE_FILE_ENTRY_SIZEATTR
130  Size = 0;
131  Attr = 0;
132  //Time = uLib::LargeInt( 0 );
133  Time = uLib::Now( uLib::SYSTEM_TIME );
134  #endif
135 }
136 
138 : Name( NULL ), Ext( NULL )
139 {
140  _init( name, dir );
141  UNUSED( pCtx );
142 }
143 
144 FileEntry::FileEntry( LPWIN32_FIND_DATA pfd, PDirEntry dir, PScanDirCtx pCtx )
145 : Name( NULL ), Ext( NULL )
146 {
147  _init( pfd->cFileName, dir );
148  #if USE_FILE_ENTRY_SIZEATTR
149  Attr = pfd->dwFileAttributes;
150  ULARGE_INTEGER ul = {{ pfd->nFileSizeLow, pfd->nFileSizeHigh }};
151  Size = ul.QuadPart;
152  Time = pfd->ftLastWriteTime;
153  #endif
154  UNUSED( pCtx );
155 }
156 
158 {
159  if (DelAction) DelAction( this, NULL ); // Callback
160  // PONDER: [uLib:FileDir.cpp] It there a way to pass the pCtx without storing it in every FileEntry..?
161  Name = deleteStr( Name );
162 }
163 
164 bool __stdcall FileEntry::_delete( PLIST_ENTRY Entry, void* pData ) // static
165 {
166  PFileEntry pFile = (PFileEntry) Entry;
167  if (pFile) delete pFile;
168  return true;
169 }
170 
172 {
173  CSTR pNewName = newStr( newName );
174  if (pNewName)
175  {
176  deleteStr( Name );
177  Name = pNewName;
178  Ext = _tcsrchr( Name, DOT );
179  if (!Ext) Ext = _tcschr( Name, 0 );
180  }
181  return pNewName;
182 }
183 
184 bool FileEntry::RemoveFromParent() // Remove this from it's containing DLinkList
185 {
186  bool ok = (pDir != NULL && pDir->Files != NULL);
187  IF_DEBUG( if (!ok) DPrint( DP_ERROR, _F("Orphaned entry: %s\n"), Name ); )
188 
189  PLIST_ENTRY pEntry = ok ? pDir->Files->RemoveEntry( this ) : NULL;
190  return (pEntry != NULL);
191 }
192 
193 UINT FileEntry::GetPathName( TSTR Buff, UINT BufLen ) // Full path name for this file
194 {
195  UINT len = 0;
196  if (pDir) {
197  len = pDir->GetFullPath( Buff, BufLen );
198  if (len)
199  {
200  _tcscpy_s( &Buff[len], BufLen-len, Name );
201  len += (UINT)_tcslen( Name );
202  }
203  } else {
204  len = (UINT)_tcslen( Name );
205  _tcscpy_s( Buff, BufLen, Name );
206  }
207  return len;
208 }
209 
210 bool FileEntry::GetFindData( LPWIN32_FIND_DATA pFData )
211 {
212  TCHAR fullName[ MAX_PATH ];
213  bool ok = (GetPathName( fullName, dimof(fullName) ) > 0);
214  if (ok) ok = GetWin32FileData( fullName, pFData );
215  return ok;
216 }
217 
218 //==---------------------------------------------------------------------------
219 // DirEntry
220 //==---------------------------------------------------------------------------
221 
222 PFnDirAddAction DirEntry::AddAction = NULL; // static
223 PFnDirAction DirEntry::DelAction = NULL; // static
224 PFnAbortScan DirEntry::Abort = NULL; // static
225 
226 void DirEntry::_init( CSTR name, PDirEntry parent )
227 {
228  InitializeListEntry( this ); //Flink = Blink = this;
229  SubDir = Files = NULL;
230  pData = NULL;
231  pParent = parent; // Back pointer to parent directory
232  Name = newStr( name );
233  #if USE_FILE_ENTRY_SIZEATTR
234  Attr = 0;
235  #endif
236 }
237 
239 {
240  _init( name, parent );
241  UNUSED( pCtx );
242 }
243 
244 DirEntry::DirEntry( LPWIN32_FIND_DATA pfd, PDirEntry parent, PScanDirCtx pCtx )
245 {
246  _init( pfd->cFileName, parent );
247  #if USE_FILE_ENTRY_SIZEATTR
248  Attr = pfd->dwFileAttributes;
249  #endif
250  UNUSED( pCtx );
251 }
252 
254 {
255  if (DelAction) DelAction( this, NULL ); // Call while node data still exist.
256  // PONDER: [uLib:FileDir.cpp]
257  // Is there a way to pass the pCtx here without storing it in every DirEntry..?
258  // Put the pCtx in thread local storage ?
259 
260  if (SubDir)
261  {
262  SubDir->RemoveAll( DirEntry::_delete ); // Recursive...
263  delete SubDir;
264  }
265  if (Files)
266  {
267  Files->RemoveAll( FileEntry::_delete );
268  delete Files;
269  }
270  Name = deleteStr( Name );
271 }
272 
273 bool __stdcall DirEntry::_delete( PLIST_ENTRY Entry, void* pData ) // static
274 {
275  PDirEntry pDir = (PDirEntry) Entry;
276  if (pDir) delete pDir; // Note: The recursion occurs here..
277  return true;
278 }
279 
280 //#
281 //# ScanDir is the main (static) entry point for building a directory file tree
282 //#
283 
284 #if (FILEDIR_VER == 1)
286  CSTR BaseDir, UINT Depth, PDirEntry Parent, PUINT pCount, PScanDirCtx pCtx
287  )
288 {
289  PDirEntry pDir = NULL;
290  UINT nFiles = 0;
291 
292  CSTR orgDir = ChangeDirectory( BaseDir ); // Safe: orgDir allocated as needed
293  if (orgDir) // NULL means SetCurrentDirectory failed, or out of memory.
294  {
295  if (!pCtx) pCtx = &_defScanCtx;
296  if (!pCtx->Flags) pCtx->Flags = SCF_FILES| SCF_DIRS;
297  pCtx->MaxNest = Depth ? Depth : MAX_DIRECTORY_DEPTH;
298  pCtx->NestDepth = 0;
299 
300  WIN32_FIND_DATA fd = {0};
301  GetWin32FileData( BaseDir, &fd );
302  _tcscpy_s( fd.cFileName, dimof(fd.cFileName), WithoutBackslash( BaseDir )); // Full path.
303  pDir = NewDirEntry( &fd, Parent );
304  if (pDir)
305  {
306  nFiles = pDir->Scan( pCtx ); // Recursively scan the whole branch
307  #if 1 // Sort the first level of the tree
308  if (pDir->SubDir)
309  pDir->SubDir->QuickSort( __sort_DirEntry, NULL );
310  #endif
311  }
312  orgDir = DoneDirectory( orgDir );
313  }
314  if (pCount) *pCount = nFiles;
315  return pDir;
316 }
317 #else // v2++ : Per-thread current directory
318  #ifdef _WIN64
319  #define DISABLE_FS_REDIR 0
320  #elif !defined( DISABLE_FS_REDIR )
321  #if _WIN32_WINNT >= 0x0501 || defined(WINBASE_DECLARE_GET_SYSTEM_WOW64_DIRECTORY)
322  #define DISABLE_FS_REDIR 1
323  #else
324  #define DISABLE_FS_REDIR 0
325  #endif
326  #endif
327 
328  #if 1
330  CSTR BaseDir, UINT Depth, PDirEntry Parent, PUINT pCount, PScanDirCtx pCtx
331  )
332  {
333  PDirEntry pDir = NULL;
334  UINT nFiles = 0;
335 
336  if (!pCtx) pCtx = &_defScanCtx;
337  if (!pCtx->Flags) pCtx->Flags = SCF_FILES| SCF_DIRS;
338  pCtx->PathEnd = pCtx->CurrentDir;
339  *pCtx->PathEnd = 0;
340  pCtx->MaxNest = Depth ? Depth : MAX_DIRECTORY_DEPTH;
341  pCtx->NestDepth = 0;
342 
343  WIN32_FIND_DATA fd;
344  IF_DEBUG( memset( &fd, 0, sizeof(fd) ));
345  GetWin32FileData( BaseDir, &fd );
346  _tcsncpyz( fd.cFileName, WithoutBackslash( BaseDir ), dimof(fd.cFileName) );
347 
348  pDir = NewDirEntry( &fd, Parent, pCtx );
349  if (pDir)
350  {
351  #if DISABLE_FS_REDIR
352  // PONDER: Include this, or leave it up to application to call it?
353  // In the latter case, include a notice in ScanDir doc.
354 
355  //PVOID prdVal;
356  //BOOL prdOk = Wow64DisableWow64FsRedirection( &prdVal );
357  #endif
358 
359  nFiles = pDir->Scan( pCtx ); // Recursively scan the whole branch
360 
361  #if DISABLE_FS_REDIR
362  //prdOk = Wow64RevertWow64FsRedirection( prdVal );
363  #endif
364  #if 1 // Sort the first level of the tree
365  if (pDir->SubDir)
366  pDir->SubDir->QuickSort( __sort_DirEntry, NULL );
367  #endif
368  }
369 
370  if (pCount) *pCount = nFiles;
371  return pDir;
372  }
373  #else //SCF_SPLIT
375  CSTR BaseDir, UINT Depth, PDirEntry Parent, PUINT pCount, PScanDirCtx pCtx
376  )
377  {
378  PDirEntry pDir = NULL;
379  UINT nFiles = 0;
380 
381  if (!pCtx) pCtx = &_defScanCtx;
382  if (!pCtx->Flags) pCtx->Flags = SCF_FILES| SCF_DIRS;
383  pCtx->PathEnd = pCtx->CurrentDir;
384  *pCtx->PathEnd = 0;
385  pCtx->MaxNest = Depth ? Depth : MAX_DIRECTORY_DEPTH;
386  pCtx->NestDepth = 0;
387 
388  WIN32_FIND_DATA fd = {0};
389  if (!HAVE_BITS( pCtx->Flags, SCF_SPLIT))
390  {
391  GetWin32FileData( BaseDir, &fd );
392  _tcsncpyz( fd.cFileName, WithoutBackslash( BaseDir ), dimof(fd.cFileName) );
393  pDir = NewDirEntry( &fd, Parent, pCtx );
394  if (pDir)
395  {
396  nFiles = pDir->Scan( pCtx ); // Recursively scan the whole branch
397 
398  #if 1 // Sort the first level of the tree
399  if (pDir->SubDir)
400  pDir->SubDir->QuickSort( __sort_DirEntry, NULL );
401  #endif
402  }
403  }
404  else // SCF_SPLIT
405  {
406  // Still under consideration...
407  // Split composite root into subdir hierarchy
408  // Create empty dir nodes for each subpath in Basedir,
409  // and commence scanning at the last one (Basedir).
410  // Finally return ptr to the first (root) dirnode created.
411  }
412 
413  if (pCount) *pCount = nFiles;
414  return pDir;
415  }
416  #endif
417 #endif
418 
419 // Used by DirEntry::Scan to log access errors.
420 
421 static void LogScanError( CSTR Op, CSTR subDir, PScanDirCtx pCtx )
422 {
423  DWORD err = GetLastError();
424  CSTR pathBuf = pCtx->CurrentDir;
425  *pCtx->PathEnd = 0; // Cut away "*.*" temporarily
426 
427  // FIXME: LogScanError is susceptible to oversize pathnames and may go GPF.
428  // F.ex: GPF occurred after encountering a "recursive symlink" file structure flaw..
429 
430  if (!pCtx->ErrorDirs) pCtx->ErrorDirs = new DLinkList;
431  if (pCtx->ErrorDirs) pCtx->ErrorDirs->Append( new StringEntry( pathBuf, err ));
432 
433  TRACE( DP_ERROR, _T("%s (%s) failed: %s\n"), Op, pathBuf, SysErrorMsg( err ));
434  *pCtx->PathEnd = _T('*'); // Put it back
435 }
436 
437 // PONDER: Change doc strategy to put lengthy details in the .cpp rather than the .h ??
438 
439 //-! <p> DirEntry::Scan is the workhorse of the FileDir hierarchy.\n
440 //-! It recursively scans the path specified in the constructor, to the depth\n
441 //-! specified in pCtx, adding files and subdirectories to the appropriate lists.\n
442 //-! The creation of entries are handled by NewDirEntry/NewFileEntry function pointers,\n
443 //-! thus enabling polymorphism or entry filtering by using customized functions.\n
444 //-! In addition, new entries can have optional post-creation action functions\n
445 //-! invoked, to perform additional initialization for descendant custom entries,\n
446 //-! and the scan is abortable by an installable function.\n
447 
449 {
450  // Performance profile: DirEntry overhead is small.
451  // The lion's share of CPU time is FindFirstFile/FindMoreFiles.
452  // Stats from Xeon E3 @ 3.1/3.4 GHz:
453  // FindFirstFile takes ~112.8 QPC ticks, and FindMoreFiles ~44.7 ticks.
454  // Adding a DirEntry takes ~4.68 QPC ticks (mostly malloc).
455  // Adding a FileEntry takes ~4.124 QPC ticks (mostly malloc).
456 
457  HANDLE hf; // FindFile handle.
458  WIN32_FIND_DATA fd;
459 
460  UINT N = 0;
461  pCtx->NestDepth++;
462  if (pCtx->Flags && (pCtx->NestDepth <= pCtx->MaxNest))
463  {
464  // Update per-context current dir..
465 
466  CSTR dirName = WithBackslash( Name );
467  TSTR pathEnd = pCtx->PathEnd;
468  long int ccRemain = dimof(pCtx->CurrentDir) - CharIndex( pCtx->CurrentDir, pathEnd );
469  // Check that there's enough buffer space left over...
470  if (ccRemain < ((long)_tcslen( dirName ) + 4 )) // +4 since we need to add a "*.*" and NUL.
471  {
472  TRACE( DP_ERROR, _F("Concatenated path too long: %s + %s\\*.*\n"), pCtx->CurrentDir, dirName );
473  BREAK();
474  goto done_Scan; // Bypasses init of 'hf'. That's ok, since we're not gonna touch it.
475  }
476  pCtx->PathEnd = _tcsnecpy( pathEnd, dirName, ccRemain );
477  _tcsncpyz( pCtx->PathEnd, ANY_FNAME, ccRemain - _tcslen( dirName )); // add "*.*"
478 
479  // Enter recursive scanner..
480 
481  hf = CheckHandle( FindFirstFile( pCtx->CurrentDir, &fd )); // ~112.8 QPC ticks
482  if (!hf)
483  LogScanError(_T("[DirEntry::Scan] FindFirstFile"), NULL, pCtx );
484  else do {
485  if (IsDir( &fd )) {
486  if (pCtx->Flags & SCF_DIRS) {
487  if (IsSubdirName( fd.cFileName )) // Exclude "." and ".."
488  {
489  PDirEntry pDir = NewDirEntry( &fd, this, pCtx );
490  if (pDir)
491  {
492  if (!SubDir) SubDir = new DLinkList();
493  SubDir->Append( pDir );
495  {
496  if (!DirEntry::AddAction( pDir, &fd, pCtx )) // Callback
497  {
498  pCtx->Flags &= ~SCF_DIRS; // Stop adding DirEntries
499  goto done_Scan;
500  }
501  }
502  N += pDir->Scan( pCtx ); // RECURSION
503  }
504  }
505  }
506  } else {
507  if (pCtx->Flags & SCF_FILES)
508  {
509  PFileEntry pFile = NewFileEntry( &fd, this, pCtx );
510  if (pFile)
511  {
512  if (!Files) Files = new DLinkList();
513  Files->Append( pFile );
515  {
516  if (!FileEntry::AddAction( pFile, &fd, pCtx )) // Callback
517  {
518  pCtx->Flags &= ~SCF_FILES; // Stop adding FileEntries
519  goto done_Scan;
520  }
521  }
522  N++;
523  }
524  }
525  }
526  if (Abort) if (Abort( this, pCtx )) break;
527 
528  } while( FindMoreFiles( hf, &fd /*,3*/ )); // ~44.7 ticks
529  done_Scan:
530  pCtx->PathEnd = pathEnd;
531  *pathEnd = 0;
532  }
533  pCtx->NestDepth--;
534  return N;
535 }
536 
538 {
539  CSTR pName;
540  if (pParent) pName = Name; // Subdirs don't have a composite path
541  else
542  {
543  pName = _tcsrchr( Name, BSLASH ); // Base dirs may have composite path.
544  #if 1
545  if (pName) pName++; else pName = Name; // Last subpath directory name.
546  #else
547  // Cope with cases where Name could have a terminal backslash.
548  // Note: ScanDir always removes any terminal backslash from a base dir,
549  // making this just a forward precution against future mishaps.
550  if (!pName) pName = Name;
551  else
552  {
553  if (!*(++pName)) // Name with a terminal backslash produce an empty string.
554  {
555  TSTR pz = (TSTR) pName - 2; // Don't wanna mess up Name, so keep BSLASH
556  while( *pz != BSLASH && pz > Name ) pz--;
557  pName = pz + 1;
558  }
559  }
560  #endif
561  }
562  return pName;
563 }
564 
566 
567 // DirEntry::GetFullPath - Get full path (from base) for this dir.
568 // Always end it with a backslash.
569 
570 static UINT _add_DirEntryToPath( PDirEntry pDir, TSTR& pDst, INT& remain )
571 {
572  // Copy dir name to the end of the buffer.
573  // NOTA BENE: Can *not* use strcpy_s for this, since it clobbers the buffer.
574 
575  UINT N = (UINT)_tcslen( pDir->Name );
576  if ((remain -= N) < 0) return 0;
577 
578  pDst -= N; // Move back to subpath insert point.
579  _tcsncpy( pDst, pDir->Name, N ); // Add subpath (excl. terminator)
580  if (pDir->pParent) *--pDst = BSLASH; // Prepend with backslash
581 
582  return N;
583 }
584 
585 UINT DirEntry::GetFullPath( TSTR Buffer, UINT BufLen )
586 {
587  if (!Buffer || !BufLen) {
588  SetLastError( ERROR_INVALID_PARAMETER );
589  return 0;
590  }
591  // We'll build the concatenated path at the *end* of the buffer,
592  // proceeding backwards for each level, and finally copy the
593  // resulting path to the front of the buffer.
594 
595  PDirEntry pPrev;
596  INT remain = BufLen-1;
597  TSTR pDst = Buffer + remain; // Point to *end* of buffer
598 
599  *pDst-- = 0;
600  *pDst = BSLASH; // Add terminal backslash.
601 
602  if (!_add_DirEntryToPath( this, pDst, remain ))
603  goto gfp_Failure;
604 
605  // Backtrack to base DirEntry (which has pParent == NULL),
606  // prepending each part in front of the previous.
607 
608  pPrev = pParent;
609  while( pPrev )
610  {
611  if (!_add_DirEntryToPath( pPrev, pDst, remain ))
612  goto gfp_Failure;
613  pPrev = pPrev->pParent;
614  }
615 
616  _tcscpy( Buffer, pDst ); // Copy result to front of buffer.
617  // NOTA BENE: Can *not* use _tcscpy_s for this, since it clobbers the buffer.
618  return (UINT)_tcslen( Buffer );
619 
620 gfp_Failure:
621  *Buffer = 0;
622  SetLastError( ERROR_INSUFFICIENT_BUFFER );
623  return 0;
624 }
625 
627 
628 //==---------------------------------------------------------------------------
629 
630 static bool __stdcall __count_Dirs( PLIST_ENTRY Entry, void* pData )
631 {
632  PDirEntry pDir = (PDirEntry) Entry;
633  IF_DEBUG( if (!pDir) DebugBreak(); ) // Should not be possible
634 
635  if (pDir && pDir->SubDir)
636  {
637  *(PUINT)pData += pDir->SubDir->Count;
638  pDir->SubDir->ForEach( __count_Dirs, pData );
639  }
640  return true;
641 }
642 UINT DirEntry::GetBranchCount() // Get recursive count of subdirectories (excl this).
643 {
644  UINT nDirs = 0; // Exclude this directory
645  __count_Dirs( this, &nDirs );
646  return nDirs;
647 }
648 
649 static bool __stdcall __count_Files( PLIST_ENTRY Entry, void* pData )
650 {
651  PDirEntry pDir = (PDirEntry) Entry;
652  IF_DEBUG( if (!pDir) DebugBreak(); )
653  // pDir NULL is only possible if someone forces it
654  // in on purpose, but better safe than sorry.
655  if (pDir)
656  {
657  if (pDir->Files)
658  *(PUINT)pData += pDir->Files->Count;
659  if (pDir->SubDir)
660  pDir->SubDir->ForEach( __count_Files, pData );
661  }
662  return true;
663 }
664 UINT DirEntry::GetFileCount() // Get recursive count of files.
665 {
666  UINT nFiles = 0;
667  __count_Files( this, &nFiles );
668  return nFiles;
669 }
670 
671 static bool __stdcall __accumulate_FileSize( PLIST_ENTRY Entry, void* pData )
672 {
673  PFileEntry pFile = (PFileEntry) Entry;
674  *(PUINT64)pData += pFile->Size;
675  return true;
676 }
677 UINT64 DirEntry::GetSizeOfFiles() // Accumulated size of contained files.
678 {
679  UINT64 size = 0;
680  if (Files) Files->ForEach( __accumulate_FileSize, &size );
681  return size;
682 }
683 
684 static bool __stdcall __accumulate_DirSize( PLIST_ENTRY Entry, void* pData )
685 {
686  PDirEntry pDir = (PDirEntry) Entry;
687  PUINT64 pSize = (PUINT64) pData;
688  *pSize += pDir->GetSizeOfFiles();
689  if (pDir->SubDir) pDir->SubDir->ForEach( __accumulate_DirSize, pSize ); // Recurse
690  return true;
691 }
692 UINT64 DirEntry::GetSizeOfBranch() // Total recursive size of branch files.
693 {
694  UINT64 size = 0;
695  __accumulate_DirSize( this, &size );
696  return size;
697 }
698 
700 {
701  PDirEntry Entry = NULL;
702  WIN32_FIND_DATA fd;
703  memset( &fd, 0, sizeof(fd) );
704 
705  _tcscpy_s( fd.cFileName, dimof(fd.cFileName), pzDir );
706  fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
707 
708  Entry = NewDirEntry( &fd, this, pCtx );
709  if (Entry)
710  {
711  if (!SubDir) SubDir = new DLinkList();
712  SubDir->Append( Entry );
713  }
714  return Entry;
715 }
716 
717 PFileEntry DirEntry::AddFile( LPWIN32_FIND_DATA pFd, PScanDirCtx pCtx )
718 {
719  PFileEntry Entry = NewFileEntry( pFd, this, pCtx );
720  if (Entry)
721  {
722  if (!Files) Files = new DLinkList();
723  Files->Append( Entry );
724  }
725  return Entry;
726 }
727 
728 bool DirEntry::RemoveFromParent() // Remove this from it's containing DLinkList
729 {
730  bool ok = (pParent != NULL && pParent->SubDir != NULL);
731  IF_DEBUG( if (!ok) DPrint( DP_ERROR, _F("Orphaned entry: %s\n"), Name );)
732 
733  if (ok) ok = (this == pParent->SubDir->RemoveEntry( this ));
734  if (!ok) SetLastError( ERROR_INVALID_BLOCK );
735  // ERROR_INVALID_BLOCK, ERROR_INVALID_DATA, ERROR_INVALID_ADDRESS,
736  // ERROR_OBJECT_NO_LONGER_EXISTS, ERROR_DS_NOT_ON_BACKLINK,
737  return ok;
738 }
739 
740 //==---------------------------------------------------------------------------
741 // Non-member APIs
742 //==---------------------------------------------------------------------------
743 
744 static CSTR nextPathSep( CSTR Path )
745 {
746  CSTR pzEnd = _tcschr( Path, BSLASH );
747  if (!pzEnd) pzEnd = _tcschr( Path, SLASH );
748  if (!pzEnd) pzEnd = _tcschr( Path, 0 );
749  return pzEnd;
750 }
751 
752 // Oops... Forgot about PathCommonPrefix()..
753 // This produces the same result as PathCommonPrefix(), except when only the
754 // drive letter is common, LongestCommonPath does not include the backslash.
755 
756 CSTR LongestCommonPath( CSTR PathName1, CSTR PathName2 )
757 {
758  static TCHAR szBuf[ MAX_PATH ]; // Return
759 
760  CSTR pzPath1, pzPath2, pzEnd1, pzEnd2;
761  UINT ccPath1, ccPath2;
762  UINT ccMatch = 0;
763 
764  pzPath1 = pzEnd1 = PathName1;
765  pzPath2 = pzEnd2 = PathName2;
766 
767  while( true )
768  {
769  pzEnd1 = nextPathSep( pzEnd1 );
770  pzEnd2 = nextPathSep( pzEnd2 );
771  ccPath1 = (UINT) CharIndex( pzPath1, pzEnd1 );
772  ccPath2 = (UINT) CharIndex( pzPath2, pzEnd2 );
773 
774  if ((ccPath1 != ccPath2) || !ccPath1 || !ccPath2) break;
775  if (_tcsnicmp( pzPath1, pzPath2, ccPath1 ) != 0) break;
776  ccMatch += ccPath1;
777 
778  pzPath1 = pzEnd1; if (*pzEnd1) pzEnd1++;
779  pzPath2 = pzEnd2; if (*pzEnd2) pzEnd2++;
780  }
781  #if _DEBUG
782  memset( szBuf, 0, sizeof(szBuf) );
783  #else
784  szBuf[0] = 0;
785  #endif
786  if (ccMatch >= dimof(szBuf)) SetLastError( ERROR_BUFFER_OVERFLOW );
787  else if (!ccMatch) SetLastError( ERROR_NOT_FOUND ); // ERROR_BAD_PATHNAME, ERROR_MOD_NOT_FOUND,
788  else _tcsncpyz( szBuf, PathName1, ccMatch+1 );
789 
790  return szBuf;
791 }
792 
793 //==---------------------------------------------------------------------------
794 // EOF
unsigned long DWORD
Definition: Common.h:414
#define DOT
Definition: Common.h:1216
#define HAVE_BITS(var, mask)
Definition: Common.h:1018
HANDLE CheckHandle(HANDLE Hnd)
Definition: KernelUtil.cpp:75
UINT GetFileCount()
Definition: FileDir.cpp:664
PDirEntry __stdcall __new_DirEntry(LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx)
Definition: FileDir.cpp:44
bool(__stdcall * PFnDirAddAction)(PDirEntry pDir, LPWIN32_FIND_DATA pFD, PScanDirCtx pCtx)
Definition: FileDir.h:55
TCHAR CurrentDir[MAX_PATH]
Definition: FileDir.h:229
#define CSTR
Definition: Common.h:329
CSTR WithoutBackslash(CSTR PathName)
Definition: StrFunc.cpp:668
CSTR WithBackslash(CSTR PathName)
Definition: StrFunc.cpp:682
virtual ~FileEntry()
Definition: FileDir.cpp:157
PDirEntry pDir
Definition: FileDir.cpp:50
#define IF_DEBUG(code)
Definition: Debug.h:236
UINT Scan(PScanDirCtx pCtx)
Definition: FileDir.cpp:448
UINT GetFullPath(TSTR Buff, UINT BufLen)
Definition: FileDir.cpp:585
#define DP_DEBUG
Definition: Debug.h:85
CSTR newStr(CSTR Src)
Definition: StrFunc.cpp:167
PDirEntry pDir
Definition: FileDir.h:136
CSTR Rename(CSTR newName)
Definition: FileDir.cpp:171
#define BEGIN_STRSAFE_OVERRIDE
Definition: StrFunc.h:28
CSTR Name
Definition: FileDir.h:128
DirEntry(LPWIN32_FIND_DATA pFd, PDirEntry pParent=NULL, PScanDirCtx pCtx=NULL)
Definition: FileDir.cpp:244
#define TSTR
Definition: Common.h:328
#define dimof(x)
Definition: Common.h:949
bool IsSubdirName(CSTR Dir)
Definition: IoUtil.cpp:43
void __cdecl DPrint(int Level, CSTR Fmt,...)
Definition: Debug.cpp:134
ScanDirCtx(UINT Depth=0)
Definition: FileDir.cpp:19
CSTR DoneDirectory(CSTR prevDir)
Definition: IoUtil.cpp:1040
#define TRACE(_lvl,...)
Definition: Debug.h:216
struct _LIST_ENTRY * PLIST_ENTRY
PVOID pData
Definition: FileDir.h:279
bool(__stdcall * PFnFileAddAction)(PFileEntry pFile, LPWIN32_FIND_DATA pFD, PScanDirCtx pCtx)
Definition: FileDir.h:56
static PFnFileAction DelAction
Definition: FileDir.h:148
static bool __stdcall _delete(PLIST_ENTRY Entry, void *pData)
Definition: FileDir.cpp:164
PDLinkList ErrorDirs
Definition: FileDir.h:241
static bool __stdcall _dump(PLIST_ENTRY Entry, void *pData)
Definition: FileDir.cpp:89
PDirEntry AddSubDir(CSTR pzDir, PScanDirCtx pCtx)
Definition: FileDir.cpp:699
virtual ~DirEntry()
Definition: FileDir.cpp:253
TSTR PathEnd
Definition: FileDir.h:230
bool(__stdcall * PFnDirAction)(PDirEntry pDir, PScanDirCtx pCtx)
Definition: FileDir.h:64
static PDirEntry ScanDir(CSTR BaseDir, UINT Depth=0, PDirEntry Parent=NULL, PUINT pCount=NULL, PScanDirCtx pCtx=NULL)
Definition: FileDir.cpp:329
#define BREAK()
Definition: Debug.h:208
bool(__stdcall * PFnAbortScan)(PDirEntry pDir, PScanDirCtx pCtx)
Definition: FileDir.h:70
UINT64 GetSizeOfBranch()
Definition: FileDir.cpp:692
const LPCTSTR CCSTR
Definition: Common.h:335
PVOID pData
Definition: FileDir.h:137
PFileEntry(__stdcall *NewFileEntry)(LPWIN32_FIND_DATA pfd
#define SCF_FILES
Definition: FileDir.h:253
CSTR SysErrorMsg(DWORD Err=0, TSTR Buf=NULL, UINT Length=0)
Definition: Debug.cpp:39
static bool __stdcall _delete(PLIST_ENTRY This, PVOID Ctx)
Definition: StrFunc.h:684
~ScanDirCtx()
Definition: FileDir.cpp:30
static bool __stdcall _delete(PLIST_ENTRY Entry, void *pData)
Definition: FileDir.cpp:273
bool IsDir(LPWIN32_FIND_DATA pFd)
Definition: IoUtil.cpp:38
PDirEntry(__stdcall *NewDirEntry)(LPWIN32_FIND_DATA pfd
CSTR GetName()
Definition: FileDir.cpp:537
PFileEntry __stdcall NewFileEntry(LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx)
PDLinkList Files
Definition: FileDir.h:276
UINT NestDepth
Definition: FileDir.h:226
unsigned __int64 UINT64
Definition: Common.h:400
UTC : GetSystemTime().
Definition: UtilFunc.h:712
bool(__stdcall * PFnFileAction)(PFileEntry pFile, PScanDirCtx pCtx)
Definition: FileDir.h:65
PDLinkList SubDir
Definition: FileDir.h:275
#define DP_ERROR
Definition: Debug.h:82
PVOID UserData
Definition: FileDir.h:234
ScanDirCtx _defScanCtx
Definition: FileDir.cpp:17
CSTR Name
Definition: FileDir.h:271
#define UNUSED(x)
Definition: Common.h:970
UINT64 GetSizeOfFiles()
Definition: FileDir.cpp:677
static PFnAbortScan Abort
Definition: FileDir.h:294
UINT MaxNest
Definition: FileDir.h:226
Debug and error handling support.
#define _tcsnecpy
Definition: StrFunc.h:75
CSTR LongestCommonPath(CSTR PathName1, CSTR PathName2)
Definition: FileDir.cpp:756
INT_PTR CharIndex(const chrType *Buffer, const chrType *Inside)
Definition: StrFunc.h:93
PDirEntry PScanDirCtx pCtx
Definition: FileDir.cpp:51
UINT GetBranchCount()
Definition: FileDir.cpp:642
FileEntry(LPWIN32_FIND_DATA pFd, PDirEntry Dir, PScanDirCtx pCtx=NULL)
Definition: FileDir.cpp:144
WORD Flags
Definition: FileDir.h:227
bool GetWin32FileData(CSTR PathName, LPWIN32_FIND_DATA pFd)
Definition: IoUtil.cpp:84
FILETIME Now(eTimeType Domain)
Definition: KernelUtil.cpp:549
#define InitializeListEntry(E)
Definition: ListFunc.h:88
UINT GetPathName(TSTR Buffer, UINT BufLen)
Definition: FileDir.cpp:193
#define BSLASH
Definition: Common.h:1217
PDirEntry __stdcall NewDirEntry(LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx)
PFileEntry AddFile(LPWIN32_FIND_DATA pFd, PScanDirCtx pCtx)
Definition: FileDir.cpp:717
bool FindMoreFiles(HANDLE hFind, WIN32_FIND_DATA *Found, OPTIN BYTE nTries=1, OPTIN BYTE msWait=50)
#define SLASH
Definition: Common.h:1218
#define _F(s)
Definition: Debug.h:49
unsigned __int64 * PUINT64
Definition: Common.h:400
PDirEntry pParent
Definition: FileDir.h:278
static PFnDirAction DelAction
Definition: FileDir.h:290
CSTR deleteStr(CSTR Dup)
Definition: StrFunc.cpp:191
bool RemoveFromParent()
Definition: FileDir.cpp:184
bool GetFindData(LPWIN32_FIND_DATA pData)
Definition: FileDir.cpp:210
CSTR Ext
Definition: FileDir.h:129
#define END_STRSAFE_OVERRIDE
Definition: StrFunc.h:29
PFileEntry __stdcall __new_FileEntry(LPWIN32_FIND_DATA pfd, PDirEntry pDir, PScanDirCtx pCtx)
Definition: FileDir.cpp:41
static bool __stdcall _dump(PLIST_ENTRY Entry, void *pData)
Definition: FileDir.cpp:74
#define MAX_DIRECTORY_DEPTH
Definition: FileDir.h:259
CSTR ChangeDirectory(CSTR Dir)
Definition: IoUtil.cpp:1026
static PFnFileAddAction AddAction
Definition: FileDir.h:143
static PFnDirAddAction AddAction
Definition: FileDir.h:285
bool RemoveFromParent()
Definition: FileDir.cpp:728
#define _tcsncpyz
Definition: StrFunc.h:77
#define SCF_DIRS
Definition: FileDir.h:254