7 #define MAX_WILDCARDS 10
9 // Internal functions only
10 BOOL
AddFileToSearchResult(PDIRECTORY_INFO pDirectory
, char *BasePathName
, char *Filename
);
11 BOOL
MergeSearchResults(PDIRECTORY_INFO pBaseDir
, PDIRECTORY_INFO pSubDir
);
12 BOOL
MergeSearchResultsAndClose(PDIRECTORY_INFO pBaseDir
, PDIRECTORY_INFO pSubDir
);
13 PSEARCH_RESULTS
GetLastSearchResult(PSEARCH_RESULTS pSearchResult
);
14 DWORD
GetSearchResultCount(PSEARCH_RESULTS pSearchResult
);
15 void DumpSearchResults(PSEARCH_RESULTS pSearchResults
);
17 BOOL
IsSameExtension(char *Extension1
, char *Extension2
)
19 if (_stricmp(Extension1
, Extension2
) == 0) return TRUE
;
23 void PrintWildcardError()
25 fprintf(stderr
, "Error: invalid wildcards for filename\n"
27 "\tfilename match \"filename\"\n"
28 "\tfilename.ext match \"filename.ext\"\n"
29 "\t* match all files (with or without extensions)\n"
30 "\t*.* match all files with extensions\n"
31 "\t*. match all files without extensions\n"
32 "\t*.ext match all files ending in \".ext\"\n"
33 "\t*.ext1;*.ext2 match all files ending in \".ext1\" or \".ext2\"\n");
36 PDIRECTORY_INFO
DirectoryOpen(char *Directory
, BOOL RecurseSubDirs
)
38 PDIRECTORY_INFO pDirectory
;
39 char tmpdir
[MAX_PATH
+1];
41 if (!Directory
) return NULL
;
42 //printf("Opening directory %s (RecurseSubDirs = %d)\n", Directory, RecurseSubDirs);
44 pDirectory
= (PDIRECTORY_INFO
)malloc(sizeof(DIRECTORY_INFO
));
47 fprintf(stderr
, "Unable to allocate %d bytes\n", sizeof(DIRECTORY_INFO
));
51 _snprintf(tmpdir
, sizeof(tmpdir
), "%s\\*", Directory
);
52 pDirectory
->hFileList
= FindFirstFile(tmpdir
, &pDirectory
->BasePath
);
53 if (pDirectory
->hFileList
== INVALID_HANDLE_VALUE
)
55 fprintf(stderr
, "Invalid path \"%s\"\n", Directory
);
59 pDirectory
->Initialized
= TRUE
;
60 pDirectory
->Finished
= FALSE
;
61 pDirectory
->RecurseSubDirs
= RecurseSubDirs
;
62 pDirectory
->pSearchResults
= NULL
;
64 _snprintf(pDirectory
->BasePathName
, sizeof(pDirectory
->BasePathName
), "%s", Directory
);
68 void DirectoryClose(PDIRECTORY_INFO pDirectory
)
70 PSEARCH_RESULTS pLast
;
72 if (!pDirectory
|| !pDirectory
->Initialized
) return;
73 //printf("Closing %s\n", pDirectory->BasePathName);
75 // Free the search results, if any
76 if (pDirectory
->pSearchResults
)
78 assert(pDirectory
->pSearchResults
->Previous
== NULL
);
80 pLast
= GetLastSearchResult(pDirectory
->pSearchResults
);
82 while (pLast
->Previous
)
84 assert(pLast
->FilePath
);
87 //printf("Deallocating entry for %s\n", pLast->FilePath);
88 free(pLast
->FilePath
);
89 pLast
->FilePath
= NULL
;
91 pLast
= pLast
->Previous
;
96 assert(pDirectory
->pSearchResults
== pLast
);
97 assert(pDirectory
->pSearchResults
->FilePath
);
99 if (pDirectory
->pSearchResults
->FilePath
)
101 //printf("Deallocating entry for %s\n", pDirectory->pSearchResults->FilePath);
102 free(pDirectory
->pSearchResults
->FilePath
);
103 pDirectory
->pSearchResults
->FilePath
= NULL
;
106 pDirectory
->pSearchResults
->Next
= NULL
;
107 free(pDirectory
->pSearchResults
);
108 pDirectory
->pSearchResults
= NULL
;
111 FindClose(pDirectory
->hFileList
);
112 pDirectory
->Initialized
= FALSE
;
116 // This will match all files in a directory (or subdirectories if recursion is enabled)
117 // Currently, this will not match any directories or specific filenames
118 // Must use "*", "*.ext", or "*.ext1;*.ext2"
119 // This is definitely not efficient and may crash when dealing with very deep levels
120 BOOL
DirectorySearch(PDIRECTORY_INFO pDirectory
, char *FileWildcard
)
122 int i
, WildcardCount
= 0;
124 BOOL RecurseSubDirs
, MatchAllFiles
= FALSE
;
125 char tmpSubDir
[MAX_PATH
+1];
126 char *tmpWildcard
, *Wildcard
, *Wildcards
[MAX_WILDCARDS
];
127 char *BasePathName
, *Filename
, *FileExtension
, *tmpFileExtension
;
128 PDIRECTORY_INFO pNewDirectory
;
130 if (!pDirectory
|| !pDirectory
->Initialized
|| pDirectory
->Finished
) return FALSE
;
132 //////////////////////////////////////////////////////////////
137 MatchAllFiles
= TRUE
;
139 else if (!FileWildcard
[0] || strchr(FileWildcard
, ','))
141 PrintWildcardError();
146 if (!(tmpWildcard
= Wildcard
= strdup(FileWildcard
)))
148 fprintf(stderr
, "Error allocating %d bytes\n", strlen(FileWildcard
)+1);
152 while (*tmpWildcard
&& (tmpFileExtension
= strchr(tmpWildcard
, ';')))
154 *tmpFileExtension
++ = '\0';
155 if (WildcardCount
== MAX_WILDCARDS
- 1) return FALSE
;
157 if (tmpWildcard
[0] == '*' && !tmpWildcard
[1])
159 MatchAllFiles
= TRUE
;
164 if (tmpWildcard
[0] == '*' && tmpWildcard
[1] != '.')
166 PrintWildcardError();
170 Wildcards
[WildcardCount
++] = strdup(tmpWildcard
);
171 tmpWildcard
= tmpFileExtension
;
175 if (MatchAllFiles
|| (tmpWildcard
[0] == '*' && !tmpWildcard
[1]))
177 MatchAllFiles
= TRUE
;
178 for (i
= 0; i
< WildcardCount
; i
++) free(Wildcards
[i
]);
181 else if (*tmpWildcard
)
183 Wildcards
[WildcardCount
++] = strdup(tmpWildcard
);
188 //////////////////////////////////////////////////////////////
189 // Iterate through subdirectories
191 RecurseSubDirs
= pDirectory
->RecurseSubDirs
;
192 BasePathName
= pDirectory
->BasePathName
;
193 Filename
= pDirectory
->BasePath
.cFileName
;
195 //printf("=== Searching in %s (RecurseSubDirs = %d)\n", BasePathName, RecurseSubDirs);
198 IsDirectory
= pDirectory
->BasePath
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
199 FileExtension
= strrchr(Filename
, '.');
201 // Match if the currently found item is a subdirectory and recursion is enabled
202 if (IsDirectory
&& RecurseSubDirs
&& *Filename
!= '.')
204 _snprintf(tmpSubDir
, sizeof(tmpSubDir
), "%s\\%s", BasePathName
, Filename
);
205 if (!(pNewDirectory
= DirectoryOpen(tmpSubDir
, TRUE
))) return FALSE
;
206 if (!(DirectorySearch(pNewDirectory
, FileWildcard
))) return FALSE
;
207 if (!MergeSearchResults(pDirectory
, pNewDirectory
)) return FALSE
;
208 DirectoryClose(pNewDirectory
);
211 // Try to match the current file to a wildcard or filename
212 else if (!IsDirectory
)
214 //printf("Found %s\\%s\n", BasePathName, Filename);
218 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
220 else if (FileExtension
)
222 FileExtension
++; // point to one byte past "." (the file extension)
223 for (i
= 0; i
< WildcardCount
; i
++)
225 if (Wildcards
[i
][0] == '*')
227 if (!Wildcards
[i
][1] || Wildcards
[i
][2] == '*')
229 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
232 else if (Wildcards
[i
][2] && IsSameExtension(FileExtension
, Wildcards
[i
] + 2))
234 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
238 else if (IsSameExtension(Filename
, Wildcards
[i
]))
240 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
247 for (i
= 0; i
< WildcardCount
; i
++)
249 if (Wildcards
[i
][0] == '*' && Wildcards
[i
][1] == '.' && !Wildcards
[i
][2])
251 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
254 else if (IsSameExtension(Filename
, Wildcards
[i
])) // match file
256 if (!AddFileToSearchResult(pDirectory
, BasePathName
, Filename
)) return FALSE
;
264 if (!FindNextFile(pDirectory
->hFileList
, &pDirectory
->BasePath
))
266 if (GetLastError() == ERROR_NO_MORE_FILES
) break;
267 fprintf(stderr
, "FindNextFile failed: error code 0x%08lx\n", GetLastError());
272 pDirectory
->Finished
= TRUE
;
276 PSEARCH_RESULTS
GetLastSearchResult(PSEARCH_RESULTS pSearchResult
)
278 while (pSearchResult
&& pSearchResult
->Next
) pSearchResult
= pSearchResult
->Next
;
279 return pSearchResult
;
282 BOOL
AddFileToSearchResult(PDIRECTORY_INFO pDirectory
, char *BasePathName
, char *Filename
)
285 PSEARCH_RESULTS pSearchResults
;
287 ////////////////////////////////////////////////////////////////
288 // Allocate a new search result set or append to an existing one
290 if (!pDirectory
->pSearchResults
)
292 pSearchResults
= pDirectory
->pSearchResults
= malloc(sizeof(SEARCH_RESULTS
));
295 fprintf(stderr
, "Error allocating %d bytes\n", sizeof(SEARCH_RESULTS
));
299 pSearchResults
->Previous
= NULL
;
301 else // an existing entry
303 pSearchResults
= GetLastSearchResult(pDirectory
->pSearchResults
);
304 if (!(pSearchResults
->Next
= malloc(sizeof(SEARCH_RESULTS
))))
306 fprintf(stderr
, "Error: unable to allocate %d bytes\n", sizeof(SEARCH_RESULTS
));
309 pSearchResults
->Next
->Previous
= pSearchResults
;
310 pSearchResults
= pSearchResults
->Next
;
313 pSearchResults
->Next
= NULL
;
315 ////////////////////////////////////////////////////////////////
316 // Save path to filename for the search result
318 PathLength
= strlen(pDirectory
->BasePathName
) + strlen(Filename
) + 2;
319 if (PathLength
> MAX_PATH
)
321 fprintf(stderr
, "Error: File path is too large\n");
325 if (!(pSearchResults
->FilePath
= (char *)malloc(PathLength
)))
327 fprintf(stderr
, "Error: unable to allocate %d bytes\n", PathLength
);
331 sprintf(pSearchResults
->FilePath
, "%s\\%s", BasePathName
, Filename
);
332 //printf("Added %s\n", pSearchResults->FilePath);
336 BOOL
MergeSearchResults(PDIRECTORY_INFO pBaseDir
, PDIRECTORY_INFO pSubDir
)
339 PSEARCH_RESULTS pDestination
, pSource
;
341 if (!pBaseDir
|| !pSubDir
) return FALSE
;
342 pSource
= pSubDir
->pSearchResults
;
343 if (!pSource
) return TRUE
; // subdirectory is empty (don't merge)
344 assert(pSource
->Previous
== NULL
);
346 if (!pBaseDir
->pSearchResults
) // merge subdirectory into empty result set
348 pDestination
= pBaseDir
->pSearchResults
= malloc(sizeof(SEARCH_RESULTS
));
351 fprintf(stderr
, "Error allocating %d bytes\n", sizeof(SEARCH_RESULTS
));
355 pDestination
->Previous
= NULL
;
356 pDestination
->Next
= NULL
;
358 else // merge subdirectory with an existing result set
360 assert(pBaseDir
->pSearchResults
->Previous
== NULL
);
361 pDestination
= GetLastSearchResult(pBaseDir
->pSearchResults
);
362 pDestination
->Next
= malloc(sizeof(SEARCH_RESULTS
));
363 if (!pDestination
->Next
)
365 fprintf(stderr
, "Error: unable to allocate %d bytes\n", sizeof(SEARCH_RESULTS
));
369 pDestination
->Next
->Previous
= pDestination
;
370 pDestination
= pDestination
->Next
;
371 pDestination
->Next
= NULL
;
376 assert(pSource
->FilePath
!= NULL
);
377 PathLength
= strlen(pSource
->FilePath
) + 1;
378 if (!(pDestination
->FilePath
= (char *)malloc(PathLength
)))
380 fprintf(stderr
, "Error: unable to allocate %d bytes\n", PathLength
);
384 strcpy(pDestination
->FilePath
, pSource
->FilePath
);
385 //printf("Merged %s\n", pDestination->FilePath);
387 pSource
= pSource
->Next
;
390 pDestination
->Next
= malloc(sizeof(SEARCH_RESULTS
));
391 if (!pDestination
->Next
)
393 fprintf(stderr
, "Error: unable to allocate %d bytes\n", sizeof(SEARCH_RESULTS
));
397 pDestination
->Next
->Previous
= pDestination
;
398 pDestination
= pDestination
->Next
;
399 pDestination
->Next
= NULL
;
402 //printf("New base directory contains:\n");
403 //DumpSearchResults(pSubDir->pSearchResults);
407 DWORD
GetSearchResultCount(PSEARCH_RESULTS pSearchResult
)
411 if (!pSearchResult
) return 0;
412 while (pSearchResult
&& pSearchResult
->Next
)
414 pSearchResult
= pSearchResult
->Next
;
421 void DumpSearchResults(PSEARCH_RESULTS pSearchResults
)
423 PSEARCH_RESULTS tmpResult
;
425 if (!pSearchResults
) return;
427 printf("Total records: %d\n", GetSearchResultCount(pSearchResults
));
428 for (tmpResult
= pSearchResults
; tmpResult
; tmpResult
= tmpResult
->Next
)
430 if (tmpResult
->FilePath
) printf("\t%s\n", tmpResult
->FilePath
);