home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / metkit / setup.exe / EXAMPLES / CATFISH / SCANDISK.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-08  |  10.7 KB  |  331 lines

  1. //    Copyright (C) 1996, 1997 Meta Four Software.  All rights reserved.
  2. //
  3. //  Recursive directory scanner sample code
  4. //
  5. //! rev="$Id: scandisk.cpp,v 1.4 1997/05/27 00:06:14 jcw Rel $"
  6.  
  7. /////////////////////////////////////////////////////////////////////////////
  8. //
  9. //  The following two globally accesible routines are defined below:
  10. //                  
  11. //      c4_View fScanDirectories(const char* path_);
  12. //      CString fFullPath(c4_View& dirs_, int dirNum_);
  13. //      
  14. //  The fScanDirectories routine does all the work, and completely hides all
  15. //  Windows 16/32 specifics by returning a generalized catalog tree object.
  16. //  In contrast with normal C++ conventions, nearly all MetaKit objects are
  17. //  reference counted. This takes care of fully automatic cleanup after use.
  18. //  
  19. //  The structure of the object returned by fScanDirectories is as follows:
  20. //  
  21. //      Property        Type        Description
  22. //      ========        ====        ===========
  23. //          
  24. //      dirs            nested      Contains 1 record per directory
  25. //          parent      integer     Index of parent entry
  26. //          name        string      Name of this directory
  27. //          files       nested      Contains 1 record per file entry
  28. //              name    string      Name of this file
  29. //              size    string      File size
  30. //              date    integer     Modification date (DOS format 7+4+5 bits)
  31. //  
  32. //  The first directory entry (entry 0) is special: the parent is set to zero
  33. //  (points to itself), and the name is the base path name (see path_ arg).
  34. //              
  35. //  In C++ notation, a declaration for this object would be something like:
  36. //  
  37. //      struct
  38. //      {
  39. //          int parent;
  40. //          char* name;
  41. //  
  42. //          struct
  43. //          {
  44. //              char* name;
  45. //              long size;
  46. //              int date;
  47. //  
  48. //          } files [];                 // variable size, not allowed in C++
  49. //  
  50. //      } dirs [];
  51. //
  52. /////////////////////////////////////////////////////////////////////////////
  53.  
  54. #include "stdafx.h"
  55. #include "scandisk.h"
  56.  
  57. #ifndef _WIN32
  58.     #define LFNAMES // define this to support long filenames in a 16-bit app
  59. #endif
  60.  
  61. /////////////////////////////////////////////////////////////////////////////
  62. // Property definitions
  63.  
  64.     c4_ViewProp     pFiles ("files");
  65.     c4_IntProp      pParent ("parent"),
  66.                     pSize ("size"),
  67.                     pDate ("date");
  68.     c4_StringProp   pName ("name");
  69.  
  70. /////////////////////////////////////////////////////////////////////////////
  71.  
  72. bool cStatusHandler::UpdateStatus(const char* /* text */)
  73. {
  74.     MSG msg;
  75.     while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  76.     {
  77.         if (msg.message == WM_QUIT)
  78.         {
  79.             ::PostQuitMessage(msg.wParam);
  80.             return FALSE;
  81.         }
  82.         if (!AfxGetApp()->PreTranslateMessage(&msg))
  83.         {
  84.             ::TranslateMessage(&msg);
  85.             ::DispatchMessage(&msg);
  86.         }
  87.     }
  88.     AfxGetApp()->OnIdle(0);   // updates user interface
  89.     AfxGetApp()->OnIdle(1);   // frees temporary objects
  90.     
  91.     return TRUE;
  92. }
  93.  
  94. /////////////////////////////////////////////////////////////////////////////
  95. // Reconstruct the full path name from a subdirectory index in the tree
  96.  
  97. CString fFullPath(c4_View& dirs_, int dirNum_)
  98. {
  99.         // Prefix all parent dir names until the root level is reached
  100.     CString path;
  101.     for (;;)
  102.     {
  103.         path = pName (dirs_[dirNum_]) + "\\" + path;
  104.  
  105.         if (dirNum_ == 0)
  106.             break;
  107.             
  108.         dirNum_ = (int) pParent (dirs_[dirNum_]);
  109.     }
  110.  
  111.     return path; // this result always has a trailing backslash
  112. }
  113.  
  114. /////////////////////////////////////////////////////////////////////////////
  115. // There are two versions of ScanSubDir, one for Win32 and one for Win16
  116.  
  117. #ifdef _WIN32
  118.  
  119.         // Convert a Win32 filedate back to the good old DOS format
  120.     static WORD DosDate(FILETIME& ft)
  121.     {
  122.         SYSTEMTIME st;
  123.         
  124.         if (!FileTimeToSystemTime(&ft, &st))
  125.             return 0;
  126.             
  127.         return (WORD) (((st.wYear-1980) << 9) | (st.wMonth << 5) | st.wDay);
  128.     }
  129.     
  130.         // Scan subdirectory and update the directory structure
  131.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  132.     {
  133.         WIN32_FIND_DATA fd;
  134.         
  135.         HANDLE h = FindFirstFile(path_ + "*", &fd);
  136.         if (h)
  137.         {
  138.                 //  Some notes on efficiency:
  139.                 //
  140.                 //  1)  It is probably faster to fill a view with data, and
  141.                 //      then store it in a persistent field, than to modify
  142.                 //      persistent structures in place. In this example, this
  143.                 //      is needed anyway, since we also sort all filenames.
  144.                 //
  145.                 //  2)  If possible, avoid constructing rows inside a loop.
  146.                 //      For that reason, both 'prop [const]' and 'row + row'
  147.                 //      are relatively inefficient.
  148.  
  149.         //  c4_View files = pFiles (dirs_[dirNum_]);
  150.             c4_View files;
  151.             c4_Row dir, file;
  152.  
  153.             files.SetSize(0, 100); // growth granularity
  154.             
  155.             do
  156.             {
  157.                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  158.                 {
  159.                     if (fd.cFileName[0] == '.')
  160.                         continue;
  161.  
  162.                 //  dirs_.Add(pParent [dirNum_] + pName [fd.cFileName]);
  163.                     pParent (dir) = dirNum_;
  164.                     pName (dir) = fd.cFileName;
  165.                     dirs_.Add(dir);
  166.                 }            
  167.                 else
  168.                 {
  169.                 //  files.Add(pName [fd.cFileName] + pSize [fd.nFileSizeLow]
  170.                 //              + pDate [DosDate(fd.ftLastWriteTime)]);
  171.                     pName (file) = fd.cFileName;
  172.                     pSize (file) = fd.nFileSizeLow;
  173.                     pDate (file) = DosDate(fd.ftLastWriteTime);
  174.                     files.Add(file);
  175.                 }
  176.                 
  177.             } while (FindNextFile(h, &fd));
  178.             
  179.             FindClose(h);
  180.             
  181.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  182.         }
  183.     }
  184.  
  185. #else
  186.  
  187.     #include <dos.h>
  188.     
  189.         // Scan subdirectory and update the directory structure
  190.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  191.     {
  192.         _find_t fd;
  193.         
  194.         const unsigned mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
  195.         if (_dos_findfirst(path_ + "*.*", mask, &fd) == 0)
  196.         {
  197.             c4_View files;
  198.             c4_Row dir, file;
  199.  
  200.             files.SetSize(0, 100); // growth granularity
  201.             
  202.             do
  203.             {
  204.                 if (fd.attrib & _A_SUBDIR)
  205.                 {
  206.                     if (fd.name[0] == '.')
  207.                         continue;
  208.  
  209.                     pParent (dir) = dirNum_;
  210.                     pName (dir) = fd.name;
  211.                     dirs_.Add(dir);
  212.                 }            
  213.                 else
  214.                 {
  215.                     pName (file) = fd.name;
  216.                     pSize (file) = fd.size;
  217.                     pDate (file) = fd.wr_date;
  218.                     files.Add(file);
  219.                 }
  220.                 
  221.             } while (_dos_findnext(&fd) == 0);
  222.             
  223.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  224.         }
  225.     }
  226.  
  227. #endif
  228.     
  229. /////////////////////////////////////////////////////////////////////////////
  230. // The following variation supports long filenames, as used by Windows 95
  231.  
  232. #ifdef LFNAMES
  233.  
  234.     #include "lfnames.h"
  235.  
  236.         // Scan subdirectory and update the directory structure
  237.     static void ScanLongDir(c4_View& dirs_, int dirNum_, const CString& path_)
  238.     {
  239.         lfn_find_t fd;
  240.         
  241.         const int mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
  242.         if (lfn_findfirst(path_ + "*.*", mask, &fd) == 0)
  243.         {
  244.             c4_View files;
  245.             c4_Row dir, file;
  246.             
  247.             files.SetSize(0, 100); // growth granularity
  248.             
  249.             do
  250.             {
  251.                 CString s = fd.name;
  252.                 s.MakeUpper();
  253.  
  254.                     // if only upper case, then capitalize the first letter only
  255.                 if (s == fd.name && !s.IsEmpty())
  256.                 {
  257.                     char c = s[0];
  258.                     s.MakeLower();
  259.                     s.SetAt(0, c);
  260.                 }
  261.                 else
  262.                     s = fd.name;
  263.                     
  264.                 if (fd.attrib & _A_SUBDIR)
  265.                 {
  266.                     if (fd.name[0] == '.')
  267.                         continue;
  268.  
  269.                     pParent (dir) = dirNum_;
  270.                     pName (dir) = s;
  271.                     dirs_.Add(dir);
  272.                 }            
  273.                 else
  274.                 {
  275.                     pName (file) = s;
  276.                     pSize (file) = fd.size;
  277.                     pDate (file) = fd.wr_date;
  278.                     files.Add(file);
  279.                 }
  280.                 
  281.             } while (lfn_findnext(&fd) == 0);
  282.             
  283.             lfn_findclose(&fd);
  284.             
  285.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  286.         }
  287.     }
  288.  
  289. #endif
  290.  
  291. /////////////////////////////////////////////////////////////////////////////
  292. // Scan a directory tree and return a corresponding structure for it
  293.  
  294. c4_View fScanDirectories(const char* path_, cStatusHandler* handler)
  295. {
  296.         // Start with a view containing the root directory entry
  297.     c4_View dirs;
  298.     dirs.Add(pName [path_]);
  299.     
  300. #ifdef LFNAMES
  301.         // don't call lfn_api on Win 3.1x, it will crash...
  302.         // NT4 returns 0x0310, this is ok since the lfn rtns don't work on it
  303.     BOOL useLfns = _winver > 0x0311 && lfn_api(path_) != 0;
  304. #endif
  305.         
  306.         // This loop "automagically" handles the recursive traversal of all
  307.         // subdirectories. The trick is that each scan may add new entries
  308.         // at the end, causing this loop to continue (GetSize() changes!).
  309.     
  310.     for (int i = 0; i < dirs.GetSize(); ++i)
  311.     {
  312.         CString path = fFullPath(dirs, i);
  313.         
  314.         if (handler && !handler->UpdateStatus(path))
  315.             return c4_View ();
  316.         
  317. #ifdef LFNAMES
  318.       if (useLfns)
  319.         ScanLongDir(dirs, i, path);
  320.       else
  321. #endif
  322.         ScanSubDir(dirs, i, path);
  323.     }
  324.     
  325.         // The returned object contains the entire directory tree.
  326.         // Everything is automatically destroyed when no longer referenced. 
  327.     return dirs;
  328. }
  329.  
  330. /////////////////////////////////////////////////////////////////////////////
  331.