Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

524 lines
16 KiB

  1. //+------------------------------------------------------------------
  2. //
  3. // Copyright (C) 1995, Microsoft Corporation.
  4. //
  5. // File: FileEnum.cxx
  6. //
  7. // Contents: class encapsulating file enumeration, including a deep option
  8. //
  9. // Classes: CFileEnumeration
  10. //
  11. // History: Nov-93 DaveMont Created.
  12. //
  13. //-------------------------------------------------------------------
  14. #include <t2.hxx>
  15. #include <FileEnum.hxx>
  16. #if DBG
  17. extern ULONG Debug;
  18. #endif
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Member: CFileEnumerate::CFileEnumerate, public
  22. //
  23. // Synopsis: initializes data members, constructor will not throw
  24. //
  25. // Arguments: IN [fdeep] - TRUE = go into sub-directories
  26. //
  27. //----------------------------------------------------------------------------
  28. CFileEnumerate::CFileEnumerate(BOOL fdeep)
  29. : _fdeep(fdeep),
  30. _findeep(FALSE),
  31. _froot(FALSE),
  32. _fcannotaccess(FALSE),
  33. _pcfe(NULL),
  34. _pwfileposition(NULL),
  35. _handle(INVALID_HANDLE_VALUE)
  36. {
  37. ENUMERATE_RETURNS((stderr, "CFileEnumerate ctor\n"))
  38. }
  39. //+---------------------------------------------------------------------------
  40. //
  41. // Member: Dtor, public
  42. //
  43. // Synopsis: closes handles
  44. //
  45. // Arguments: none
  46. //
  47. //----------------------------------------------------------------------------
  48. CFileEnumerate::~CFileEnumerate()
  49. {
  50. if (_handle != INVALID_HANDLE_VALUE)
  51. FindClose(_handle);
  52. ENUMERATE_RETURNS((stderr, "CFileEnumerate dtor (%ws)\n", _wpath))
  53. }
  54. //+---------------------------------------------------------------------------
  55. //
  56. // Member: CFileEnumerate::Init, public
  57. //
  58. // Synopsis: Init must be called before any other methods - this
  59. // is not enforced. converts a ASCII file/path to a UNICODE
  60. // file/path, and gets the first file in the enumeration
  61. //
  62. // Arguments: IN [filename] - the path/file to enumerate
  63. // OUT [wfilename] - first file in the enumeration
  64. // OUT [fdir] - TRUE = returned file is a directory
  65. //
  66. //----------------------------------------------------------------------------
  67. ULONG CFileEnumerate::Init(CHAR *filename, WCHAR **wfilename, BOOL *fdir)
  68. {
  69. // Initialize the file name
  70. if (filename && (strlen(filename) < MAX_PATH))
  71. {
  72. // make it wchar
  73. WCHAR winfilename[MAX_PATH];
  74. if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
  75. filename, -1,
  76. winfilename, sizeof(winfilename) / sizeof(winfilename[0])) == 0)
  77. return(ERROR_INVALID_NAME);
  78. // finish initialization
  79. return(_ialize(winfilename, wfilename, fdir));
  80. }
  81. ENUMERATE_FAIL((stderr, "Init bad file name: %ld\n",ERROR_INVALID_NAME))
  82. return(ERROR_INVALID_NAME);
  83. }
  84. //+---------------------------------------------------------------------------
  85. //
  86. // Member: CFileEnumerate::Init, public
  87. //
  88. // Synopsis: Same as previous, except takes UNICODE file/path as input
  89. //
  90. // Arguments: IN [filename] - the path/file to enumerate
  91. // OUT [wfilename] - first file in the enumeration
  92. // OUT [fdir] - TRUE = returned file is a directory
  93. //
  94. //----------------------------------------------------------------------------
  95. ULONG CFileEnumerate::Init(WCHAR *filename, WCHAR **wfilename, BOOL *fdir)
  96. {
  97. // Initialize the file name
  98. if (filename && (wcslen(filename) < MAX_PATH))
  99. {
  100. return(_ialize(filename, wfilename, fdir));
  101. }
  102. ENUMERATE_FAIL((stderr, "Init bad file name: %ld\n",ERROR_INVALID_NAME))
  103. return(ERROR_INVALID_NAME);
  104. }
  105. //+---------------------------------------------------------------------------
  106. //
  107. // Member: CFileEnumerate::_ialize, private
  108. //
  109. // Synopsis: finishes initialization and starts search for first file in
  110. // the enumeration
  111. //
  112. // Arguments: OUT [wfilename] - first file in the enumeration
  113. // OUT [fdir] - TRUE = returned file is a directory
  114. //
  115. //----------------------------------------------------------------------------
  116. ULONG CFileEnumerate::_ialize(WCHAR *winfilename, WCHAR **wfilename, BOOL *fdir)
  117. {
  118. ENUMERATE_RETURNS((stderr, "Init start, path = %ws\n", winfilename))
  119. ULONG ret = ERROR_SUCCESS;
  120. ENUMERATE_STAT((stderr, "start path = %ws\n",winfilename))
  121. // save the location of the filename or wildcards
  122. ULONG cwcharcount;
  123. if (!(cwcharcount = GetFullPathName(winfilename,
  124. MAX_PATH,
  125. _wpath,
  126. &_pwfileposition)))
  127. {
  128. return(ERROR_INVALID_NAME);
  129. }
  130. ENUMERATE_STAT((stderr, "got full path name = %ws, filename = (%ws), total chars = %d\n",_wpath, _pwfileposition, cwcharcount))
  131. // if the filepart (_pwfileposition) is NULL, then the name must end in a slash.
  132. // add a *
  133. if (NULL == _pwfileposition)
  134. {
  135. _pwfileposition = (WCHAR *)Add2Ptr(_wpath,wcslen(_wpath)*sizeof(WCHAR));
  136. }
  137. // save the filename/wildcards
  138. wcscpy(_wwildcards, _pwfileposition);
  139. ENUMERATE_EXTRA((stderr, "wild cards = %ws\n",_wwildcards))
  140. // if we are at a root (path ends in :\)
  141. if ( (_wpath[wcslen(_wpath) - 1] == L'\\') &&
  142. (wcslen(_wpath) > 1) &&
  143. (_wpath[wcslen(_wpath) - 2] == L':') )
  144. {
  145. _wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  146. _wfd.cFileName[0] = L'\0';
  147. *wfilename = _wpath;
  148. *fdir = TRUE;
  149. _froot = TRUE;
  150. } else
  151. {
  152. // check to see if we can iterate through files
  153. if ( (INVALID_HANDLE_VALUE == ( _handle = FindFirstFile(_wpath, &_wfd) ) ) )
  154. {
  155. ret = GetLastError();
  156. _fcannotaccess = (ERROR_ACCESS_DENIED == ret);
  157. ENUMERATE_FAIL((stderr, "find first returned: %ld\n",ret))
  158. }
  159. if (ERROR_SUCCESS == ret)
  160. {
  161. // reject . & .. filenames (go on to next file )
  162. if ( (0 == wcscmp(_wfd.cFileName, L".")) ||
  163. (0 == wcscmp(_wfd.cFileName, L"..")) )
  164. {
  165. ret = _NextLocal(wfilename,fdir);
  166. } else
  167. {
  168. // return the current directory
  169. if (_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  170. *fdir = TRUE;
  171. else
  172. *fdir = FALSE;
  173. // add the filename to the path so the whole thing is returned
  174. wcscpy(_pwfileposition, _wfd.cFileName);
  175. *wfilename = _wpath;
  176. }
  177. }
  178. ENUMERATE_STAT((stderr, "next filename = %ws\n", *wfilename))
  179. }
  180. // if we are going deep and we did not find a file yet:
  181. if ( _fdeep && ( ( ERROR_NO_MORE_FILES == ret ) ||
  182. ( ERROR_FILE_NOT_FOUND == ret ) ) )
  183. {
  184. if (_handle != INVALID_HANDLE_VALUE)
  185. {
  186. FindClose(_handle);
  187. _handle = INVALID_HANDLE_VALUE;
  188. }
  189. ret = _InitDir(wfilename, fdir);
  190. }
  191. ENUMERATE_RETURNS((stderr, "Init returning = %ws(%ld)\n\n", *wfilename, ret))
  192. return(ret);
  193. }
  194. //+---------------------------------------------------------------------------
  195. //
  196. // Member: CFileEnumerate::Next, public
  197. //
  198. // Synopsis: finds the next file in the enumeration
  199. //
  200. // Arguments: OUT [wfilename] - first file in the enumeration
  201. // OUT [fdir] - TRUE = returned file is a directory
  202. //
  203. //----------------------------------------------------------------------------
  204. ULONG CFileEnumerate::Next(WCHAR **wfilename, BOOL *fdir)
  205. {
  206. ENUMERATE_RETURNS((stderr, "Next start, path = %ws\n", _wpath))
  207. ULONG ret = ERROR_NO_MORE_FILES;
  208. // if we failed to initialize with an ERROR_ACCESS_DENIED, then exit
  209. if (_fcannotaccess)
  210. return(ERROR_NO_MORE_FILES);
  211. // if we are not in deep
  212. if (!_findeep)
  213. {
  214. if (!_froot)
  215. ret = _NextLocal(wfilename, fdir);
  216. // if we ran out of files and we are going deep:
  217. if ( _fdeep &&
  218. ( ( ERROR_NO_MORE_FILES == ret ) ||
  219. ( ERROR_FILE_NOT_FOUND == ret ) || _froot ) )
  220. {
  221. if (_handle != INVALID_HANDLE_VALUE)
  222. {
  223. FindClose(_handle);
  224. _handle = INVALID_HANDLE_VALUE;
  225. }
  226. ret = _InitDir(wfilename, fdir);
  227. _froot = FALSE; // (we are past the root now)
  228. }
  229. } else
  230. {
  231. // if we are already down a directory (and in deep)
  232. if (_pcfe)
  233. {
  234. if (ERROR_SUCCESS != (ret = _pcfe->Next(wfilename, fdir)))
  235. {
  236. if (ERROR_ACCESS_DENIED != ret)
  237. {
  238. delete _pcfe;
  239. _pcfe = NULL;
  240. }
  241. }
  242. }
  243. // we need to go to the next directory in the current dir
  244. if (ERROR_NO_MORE_FILES == ret)
  245. {
  246. ret = _NextDir(wfilename, fdir);
  247. }
  248. }
  249. ENUMERATE_RETURNS((stderr, "Next returning = %ws(%ld)\n\n", *wfilename, ret))
  250. return(ret);
  251. }
  252. //+---------------------------------------------------------------------------
  253. //
  254. // Member: CFileEnumerate::_NextLocal, private
  255. //
  256. // Synopsis: searchs for the next file in the current directory
  257. //
  258. // Arguments: OUT [wfilename] - first file in the enumeration
  259. // OUT [fdir] - TRUE = returned file is a directory
  260. //
  261. //----------------------------------------------------------------------------
  262. ULONG CFileEnumerate::_NextLocal(WCHAR **wfilename, BOOL *fdir)
  263. {
  264. ENUMERATE_RETURNS((stderr, "_NextLocal start, path = %ws\n", _wpath))
  265. ULONG ret = ERROR_SUCCESS;
  266. // ensure that we have a valid handle for a findnextfile
  267. if (INVALID_HANDLE_VALUE == _handle)
  268. {
  269. ret = ERROR_INVALID_HANDLE;
  270. } else
  271. {
  272. do
  273. {
  274. if (!FindNextFile(_handle, &_wfd))
  275. {
  276. ret = GetLastError();
  277. ENUMERATE_FAIL((stderr, "find next returned: %ld\n",ret))
  278. } else
  279. ret = ERROR_SUCCESS;
  280. }
  281. while ( (ERROR_SUCCESS == ret) &&
  282. ( (0 == wcscmp(_wfd.cFileName, L".")) ||
  283. (0 == wcscmp(_wfd.cFileName, L"..")) ) );
  284. // if we found a file
  285. if (ERROR_SUCCESS == ret)
  286. {
  287. // return the directory attrib.
  288. if (_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  289. *fdir = TRUE;
  290. else
  291. *fdir = FALSE;
  292. // add the filename to the path so the whole thing is returned
  293. wcscpy(_pwfileposition, _wfd.cFileName);
  294. *wfilename = _wpath;
  295. ENUMERATE_STAT((stderr, "next filename = %ws\n", *wfilename))
  296. }
  297. }
  298. ENUMERATE_RETURNS((stderr, "_NextLocal returning = %ws(%ld)\n", *wfilename, ret))
  299. return(ret);
  300. }
  301. //+---------------------------------------------------------------------------
  302. //
  303. // Member: CFileEnumerate::_InitDir, private
  304. //
  305. // Synopsis: (only called if going deep)
  306. // goes down a directory (and thus causing a new CFileEnumerator
  307. // to be created, or re-initializies
  308. //
  309. // Arguments: OUT [wfilename] - first file in the enumeration
  310. // OUT [fdir] - TRUE = returned file is a directory
  311. //
  312. //----------------------------------------------------------------------------
  313. ULONG CFileEnumerate::_InitDir(WCHAR **wfilename, BOOL *fdir)
  314. {
  315. ENUMERATE_RETURNS((stderr, "_InitDir start, path = %ws\n", _wpath))
  316. ULONG ret = ERROR_SUCCESS;
  317. // check and see if a directory was entered as the filename
  318. if ( (0 == _wcsicmp(_wwildcards, _wfd.cFileName)) &&
  319. (_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
  320. {
  321. ENUMERATE_EXTRA((stderr, "first file matched directory = %ws\n", _wpath))
  322. _pwfileposition += wcslen(_wfd.cFileName);
  323. wcscpy(_pwfileposition, L"\\*.*");
  324. _pwfileposition++;
  325. wcscpy(_wwildcards, L"*.*");
  326. ENUMERATE_EXTRA((stderr, " path = %ws\n",_wpath))
  327. ENUMERATE_EXTRA((stderr, "wild cards = %ws\n",_wwildcards))
  328. WCHAR winfilename[MAX_PATH];
  329. wcscpy(winfilename, _wpath);
  330. ret = _ialize(winfilename, wfilename, fdir);
  331. } else
  332. {
  333. // we are in deep
  334. _findeep = TRUE;
  335. // search thru all directories
  336. wcscpy(_pwfileposition, L"*.*");
  337. if (INVALID_HANDLE_VALUE == ( _handle = FindFirstFile(_wpath, &_wfd) ))
  338. {
  339. ret = GetLastError();
  340. ENUMERATE_FAIL((stderr, "find first (dir) returned: %ld\n",ret))
  341. } else
  342. {
  343. if ( !(_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
  344. (0 == wcscmp(_wfd.cFileName, L".")) ||
  345. (0 == wcscmp(_wfd.cFileName, L"..")) )
  346. {
  347. ret = _NextDir(wfilename, fdir);
  348. } else
  349. {
  350. // if we have a sub directory, go down it
  351. ret = _DownDir(wfilename, fdir);
  352. // if we found nothing in that first sub directory, go the the next one
  353. if ( (ERROR_NO_MORE_FILES == ret ) ||
  354. (ERROR_FILE_NOT_FOUND == ret ) )
  355. {
  356. ret = _NextDir(wfilename, fdir);
  357. }
  358. }
  359. }
  360. }
  361. ENUMERATE_RETURNS((stderr, "_InitDir returning = %ws(%ld)\n", *wfilename, ret))
  362. return(ret);
  363. }
  364. //+---------------------------------------------------------------------------
  365. //
  366. // Member: CFileEnumerate::_NextDir, private
  367. //
  368. // Synopsis: (only called if going deep)
  369. // finds the next sub-directory from the current directory,
  370. // and then goes down into that directory
  371. //
  372. // Arguments: OUT [wfilename] - first file in the enumeration
  373. // OUT [fdir] - TRUE = returned file is a directory
  374. //
  375. //----------------------------------------------------------------------------
  376. ULONG CFileEnumerate::_NextDir(WCHAR **wfilename, BOOL *fdir)
  377. {
  378. ENUMERATE_RETURNS((stderr, "_NextDir start, path = %ws\n", _wpath))
  379. ULONG ret = ERROR_SUCCESS;
  380. // skip the . & .. & files we cannot access
  381. if (INVALID_HANDLE_VALUE == _handle)
  382. {
  383. ret = ERROR_INVALID_HANDLE;
  384. } else
  385. {
  386. do
  387. {
  388. do
  389. {
  390. if (!FindNextFile(_handle, &_wfd))
  391. {
  392. ret = GetLastError();
  393. ENUMERATE_FAIL((stderr, "find next returned: %ld\n",ret))
  394. } else
  395. ret = ERROR_SUCCESS;
  396. }
  397. while ( (ERROR_SUCCESS == ret) &&
  398. ( !(_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
  399. (0 == wcscmp(_wfd.cFileName, L".")) ||
  400. (0 == wcscmp(_wfd.cFileName, L"..")) ) );
  401. // if we found a directory
  402. if (ERROR_SUCCESS == ret)
  403. {
  404. ret = _DownDir(wfilename, fdir);
  405. } else
  406. {
  407. // out of subdirectories to search, break out of the loop
  408. break;
  409. }
  410. }
  411. while (( ERROR_NO_MORE_FILES == ret) || (ERROR_FILE_NOT_FOUND == ret));
  412. }
  413. ENUMERATE_RETURNS((stderr, "_NextDir returning = %ws(%ld)\n", *wfilename, ret))
  414. return(ret);
  415. }
  416. //+---------------------------------------------------------------------------
  417. //
  418. // Member: CFileEnumerate::_DownDir, private
  419. //
  420. // Synopsis: (only called if going deep)
  421. // creates a new CFileEnumerator for a sub-directory
  422. //
  423. // Arguments: OUT [wfilename] - first file in the enumeration
  424. // OUT [fdir] - TRUE = returned file is a directory
  425. //
  426. //----------------------------------------------------------------------------
  427. ULONG CFileEnumerate::_DownDir(WCHAR **wfilename, BOOL *fdir)
  428. {
  429. ENUMERATE_RETURNS((stderr, "_DownDir start, path = %ws\n", _wpath))
  430. ULONG ret;
  431. // make a new file enumerator class (this one) We should only go down
  432. // 8 directories at most.
  433. _pcfe = new CFileEnumerate(_fdeep);
  434. if (!_pcfe)
  435. return ERROR_NOT_ENOUGH_MEMORY;
  436. // add the wildcards to the end of the directory we are going down
  437. wcscpy(_pwfileposition, _wfd.cFileName);
  438. wcscat(_pwfileposition, L"\\");
  439. wcscat(_pwfileposition, _wwildcards);
  440. // start it up and see if we find a match
  441. if (ERROR_SUCCESS != (ret = _pcfe->Init(_wpath, wfilename, fdir)))
  442. {
  443. if (ERROR_ACCESS_DENIED != ret)
  444. {
  445. delete _pcfe;
  446. _pcfe = NULL;
  447. }
  448. }
  449. ENUMERATE_RETURNS((stderr, "_DownDir returning = %ws(%ld)\n", *wfilename, ret))
  450. return(ret);
  451. }