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.

328 lines
19 KiB

  1. /* ---------------------------------------------------------------------------------------------------------------------------- */
  2. /* */
  3. /* DIRWAK2AC */
  4. /* */
  5. /* ---------------------------------------------------------------------------------------------------------------------------- */
  6. /* ---------------------------------------------------------------------------------------------------------------------------- */
  7. /* */
  8. /* int DirectoryWalk(context,directory,ProcessDirectory,ProcessFile,ProcessDirectoryEnd,pTree) will search a directory and all */
  9. /* of its subdirectories looking for file entries. The provided ProcessDirectory() will be called for each subdirectory found */
  10. /* just before searching that directory. ProcessDirectory() is called for the original path; an empty name is reported. */
  11. /* The provided ProcessDirectoryEnd() will be called after the last file has been reported in each directory. The provided */
  12. /* ProcessFile() is called for each file located. pTree is passed as to those functions for OEMPATCH support */
  13. /* */
  14. /* If NULL is provided for ProcessDirectory, subdirectories will not be included in the search. */
  15. /* If NULL is provided for ProcessFile, no notification of file entries will be made. */
  16. /* If NULL is provided for ProcessDirectoryEnd, no notification of end of directories will be made. */
  17. /* */
  18. /* Return codes are: */
  19. /* */
  20. /* 0 => no error. */
  21. /* DW_MEMORY => ran out of memory to track directories. */
  22. /* DW_ERROR => DOS reported an error on a search (most likely the path doesn't exist.) */
  23. /* DW_DEPTH => total path name was or became too long. */
  24. /* any others => whatever ProcessDirectory(), ProcessFile(), or ProcessDirectoryEnd() returned. */
  25. /* */
  26. /* ---------------------------------------------------------------------------------------------------------------------------- */
  27. /* ---------------------------------------------------------------------------------------------------------------------------- */
  28. /* */
  29. /* Revision 1.9a 10/04/99 JXW DIRWAK2A: added support for OEMPATCH for a one pass patching process */
  30. /* */
  31. /* Revision 1.9 10/04/99 MVS DIRWALK2: unicode, FILETIME, big FILESIZE */
  32. /* */
  33. /* Revision 1.8 05/06/99 MVS Fixed to preserve case of directories. */
  34. /* */
  35. /* Revision 1.7 03/05/99 MVS Fixed date/time handling for files with no info. Make directory sorting case-insensitive. */
  36. /* */
  37. /* Revision 1.6 09/28/97 MVS Added global context pointer. Fixed leaks in error conditions. Abandoned 16-bit version. */
  38. /* */
  39. /* Revision 1.5 12/16/96 MVS Added 32-bit file date/time conversions. */
  40. /* */
  41. /* Revision 1.4 10/14/96 MVS Fixed 32-bit case for ProcessDirectory==NULL. */
  42. /* */
  43. /* Revision 1.3 03/22/94 MVS Made NT-capable version. For non-NT, compile with -DBIT16. */
  44. /* */
  45. /* Revision 1.2 03/07/94 MVS Removed erroneous const qualifiers. Changed to new-style declarators. */
  46. /* */
  47. /* Revision 1.1 04/03/93 MVS Forced the minor list to stay sorted, so that all directories will be reported in sorted order. */
  48. /* (filenames within each directory can still appear unsorted.) */
  49. /* */
  50. /* Revision 1.0 MVS Initial release. A long, long time ago. */
  51. /* */
  52. /* ---------------------------------------------------------------------------------------------------------------------------- */
  53. #ifndef WIN32_LEAN_AND_MEAN
  54. #define WIN32_LEAN_AND_MEAN
  55. #endif
  56. #include <windows.h>
  57. #include "dirwak2a.h" /* prototype verification */
  58. #define MAXPATH 515 /* reasonable path length limit */
  59. #define AND && /* standard definition */
  60. #define OR || /* standard definition */
  61. #ifndef EXDEV
  62. #define EXDEV 0x12 /* MS-DOS "no more files" error code */
  63. #endif
  64. struct DIRECTORY /* internal use only - a directory discovered but not explored */
  65. {
  66. struct DIRECTORY *link; /* link to next to be searched */
  67. int length; /* length of path string before this name */
  68. void *parentID; /* ID of this directory's parent */
  69. WCHAR directory[1]; /* name of the directory to search / MUST BE LAST STRUCT ENTRY */
  70. };
  71. int DirectoryWalk(
  72. void *context,
  73. void *parentID,
  74. const WCHAR *directory,
  75. FN_DIRECTORY *ProcessDirectory,
  76. FN_FILE *ProcessFile,
  77. FN_DIRECTORY_END *ProcessDirectoryEnd,
  78. void *pTree)
  79. {
  80. int length = 0; /* length of current path */
  81. HANDLE findHandle; /* handle for searches */
  82. WIN32_FIND_DATAW file; /* find data structure */
  83. WCHAR path[MAXPATH+7]; /* working buffer */
  84. struct DIRECTORY *majorList; /* list of subdirectories to explore */
  85. struct DIRECTORY *minorList; /* list of subdirectories just found */
  86. struct DIRECTORY *temp; /* subdirectory list temporary */
  87. struct DIRECTORY **backLink; /* where to link next to end of the minor list */
  88. int result;
  89. majorList = NULL;
  90. minorList = NULL;
  91. findHandle = INVALID_HANDLE_VALUE;
  92. if((length = wcslen(directory)) > MAXPATH) /* check path length */
  93. {
  94. result = DW_DEPTH; /* provided path is too long */
  95. goto done;
  96. }
  97. wcscpy(path, directory); /* copy into our buffer */
  98. if(length)
  99. { /* change paths like "DOS" to "DOS\" */
  100. if((path[length-1] != L'\\') AND (path[length-1] != L':')) /* don't make "DOS\\", don't assume "C:" means "C:\" */
  101. {
  102. wcscpy(path + length, L"\\"); /* so we can attach "*.*" or another name */
  103. length++; /* that makes the length longer */
  104. }
  105. }
  106. majorList = (struct DIRECTORY*)GlobalAlloc(GMEM_FIXED, sizeof(struct DIRECTORY)); /* allocate initial request as a directory to explore */
  107. if(majorList == NULL)
  108. {
  109. result = DW_MEMORY; /* if out of memory */
  110. goto done;
  111. }
  112. majorList->link = NULL; /* fabricate initial request */
  113. majorList->directory[0] = L'\0'; /* no discovery to report yet */
  114. majorList->length = length; /* length of base path name */
  115. majorList->parentID = parentID; /* parent ID of initial request */
  116. while(majorList != NULL) /* until we run out of places to look... */
  117. {
  118. path[majorList->length] = L'\0'; /* truncate path as needed */
  119. wcscpy(path + majorList->length, majorList->directory); /* append the name from the list */
  120. parentID = majorList->parentID; /* pre-set parent ID to parent's */
  121. if(ProcessDirectory != NULL) /* if we are supposed to do subdirectories, report this one */
  122. {
  123. result = ProcessDirectory(context,
  124. majorList->parentID, /* pass ID of parent dir */
  125. majorList->directory, /* name of this directory */
  126. path, /* full path incl. this dir */
  127. &parentID, /* where to store new ID */
  128. pTree);
  129. if(result != DW_NO_ERROR) /* if ProcessDirectory reports an error, */
  130. {
  131. goto done; /* then pass it on */
  132. }
  133. }
  134. length = wcslen(path); /* compute length so we can clip later */
  135. if(length)
  136. { /* change paths like "DOS" to "DOS\" */
  137. if((path[length-1] != '\\') AND (path[length-1] != ':')) /* don't make "DOS\\", don't assume "C:" means "C:\" */
  138. {
  139. wcscpy(path + length, L"\\"); /* so we can attach "*.*" or some other name */
  140. length++; /* that increased the length by one */
  141. }
  142. }
  143. wcscat(path, L"*.*"); /* append wild file name for findfirst */
  144. findHandle = FindFirstFileW(path, &file);
  145. if(findHandle == INVALID_HANDLE_VALUE)
  146. {
  147. result = EXDEV;
  148. }
  149. else
  150. {
  151. result = DW_NO_ERROR;
  152. }
  153. path[length] = L'\0'; /* truncate name to path (clip the "*.*") */
  154. while(result == DW_NO_ERROR) /* while there are entries */
  155. {
  156. if(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* if it's a subdirectory entry */
  157. {
  158. if(ProcessDirectory != NULL)
  159. {
  160. if((file.cFileName[0] == L'.') &&
  161. (file.cFileName[1] == L'\0'))
  162. {
  163. goto skip; /* skip "." entry */
  164. }
  165. if((file.cFileName[0] == L'.') &&
  166. (file.cFileName[1] == L'.') &&
  167. (file.cFileName[2] == L'\0'))
  168. {
  169. goto skip; /* skip ".." entry */
  170. }
  171. /* A subdirectory entry has been found. Append a record to the */
  172. /* minor list and we'll process each of those after we complete */
  173. /* the current directory. This approach means that only one */
  174. /* find_t structure is in use at a time (better for DOS), all */
  175. /* files in the same directory are reported together, and we */
  176. /* don't have to search each directory again looking for the */
  177. /* subdirectory entries. */
  178. if((length + wcslen(file.cFileName)) > MAXPATH)
  179. {
  180. result = DW_DEPTH; /* directory name is getting too long */
  181. goto done;
  182. }
  183. temp = (struct DIRECTORY*)GlobalAlloc(GMEM_FIXED, sizeof(struct DIRECTORY) + sizeof(WCHAR) * wcslen(file.cFileName));
  184. if(temp == NULL) /* if ran out of memory */
  185. {
  186. result = DW_MEMORY; /* post the result */
  187. goto done;
  188. }
  189. wcscpy(temp->directory,file.cFileName); /* create new record */
  190. temp->length = length; /* remember depth */
  191. temp->parentID = parentID; /* ID of this child's parent */
  192. backLink = &minorList; /* start search at head of minor list */
  193. while((*backLink != NULL) && (_wcsicmp(temp->directory,(*backLink)->directory) > 0))
  194. {
  195. backLink = &(*backLink)->link; /* walk up the list past "smaller" names */
  196. }
  197. temp->link = *backLink; /* inherit link of previous */
  198. *backLink = temp; /* become previous' successor */
  199. skip:
  200. ;
  201. }
  202. }
  203. else /* if not a subdirectory */
  204. {
  205. /* A file entry has been found. Call the file processor and */
  206. /* let it do whatever it wants with it. */
  207. if(ProcessFile != NULL) /* call only if there is a file processor */
  208. {
  209. __int64 FileSize = (((__int64) file.nFileSizeHigh) << 32) + file.nFileSizeLow;
  210. result = ProcessFile(context,
  211. parentID, /* pass ID of parent dir */
  212. file.cFileName, /* name of file just found */
  213. path, /* full path to the file */
  214. file.dwFileAttributes, /* the file's attribute bits */
  215. file.ftLastWriteTime, /* last-modified time */
  216. file.ftCreationTime, /* creation time */
  217. FileSize,
  218. pTree);
  219. if(result != DW_NO_ERROR) /* if file processor fails, report it */
  220. {
  221. goto done; /* return whatever file processor returned to us */
  222. }
  223. }
  224. }
  225. if(!FindNextFileW(findHandle,&file)) /* look for next entry */
  226. {
  227. result = EXDEV; /* NT's only error is "no more" */
  228. }
  229. } /* end while */
  230. if(findHandle != INVALID_HANDLE_VALUE)
  231. {
  232. FindClose(findHandle);
  233. findHandle = INVALID_HANDLE_VALUE;
  234. }
  235. if(ProcessDirectoryEnd != NULL) /* if we are supposed to report end of directories */
  236. {
  237. result = ProcessDirectoryEnd(context,
  238. majorList->parentID, /* pass ID of parent dir */
  239. majorList->directory, /* name of this directory */
  240. path, /* full path incl. this dir */
  241. parentID); /* pass ID of this directory */
  242. if(result != DW_NO_ERROR) /* if ProcessDirectoryEnd reports an error, */
  243. {
  244. goto done; /* then pass it on */
  245. }
  246. }
  247. temp = majorList; /* hold pointer to destroy */
  248. majorList = majorList->link; /* advance major list to the next entry */
  249. GlobalFree(temp); /* destroy list entries as they are used */
  250. backLink = &minorList; /* locate end of the minor list */
  251. while(*backLink != NULL) /* (actually, find the element who's forward link is NULL) */
  252. {
  253. backLink = &(*backLink)->link; /* (then we'll set that element's link to the front of major list) */
  254. }
  255. *backLink = majorList; /* prepend the new minor list onto the major list */
  256. majorList = minorList; /* minor list becomes the major list, minor list is scrapped */
  257. minorList = NULL; /* reset the minor list */
  258. }
  259. result = DW_NO_ERROR;
  260. done:
  261. while(majorList != NULL) /* don't leak the list */
  262. {
  263. temp = majorList;
  264. majorList = majorList->link;
  265. GlobalFree(temp);
  266. }
  267. while(minorList != NULL) /* don't leak the list */
  268. {
  269. temp = minorList;
  270. minorList = minorList->link;
  271. GlobalFree(temp);
  272. }
  273. if(findHandle != INVALID_HANDLE_VALUE)
  274. {
  275. FindClose(findHandle);
  276. }
  277. return(result); /* report result to caller */
  278. }