Mark msysGit as obsolete
[msysgit.git] / src / depends / directory.c
blob348712a7edaacb651d89114248c84b4387a46de6
1 #include <windows.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <assert.h>
5 #include "directory.h"
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;
20 else return FALSE;
23 void PrintWildcardError()
25 fprintf(stderr, "Error: invalid wildcards for filename\n"
26 "Wildcards can be:\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));
45 if (!pDirectory)
47 fprintf(stderr, "Unable to allocate %d bytes\n", sizeof(DIRECTORY_INFO));
48 return NULL;
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);
56 return NULL;
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);
65 return pDirectory;
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);
81 assert(pLast);
82 while (pLast->Previous)
84 assert(pLast->FilePath);
85 if (pLast->FilePath)
87 //printf("Deallocating entry for %s\n", pLast->FilePath);
88 free(pLast->FilePath);
89 pLast->FilePath = NULL;
91 pLast = pLast->Previous;
92 free(pLast->Next);
93 pLast->Next = NULL;
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;
113 free(pDirectory);
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;
123 BOOL IsDirectory;
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 //////////////////////////////////////////////////////////////
133 // Setup wildcards
135 if (!FileWildcard)
137 MatchAllFiles = TRUE;
139 else if (!FileWildcard[0] || strchr(FileWildcard, ','))
141 PrintWildcardError();
142 return FALSE;
144 else
146 if (!(tmpWildcard = Wildcard = strdup(FileWildcard)))
148 fprintf(stderr, "Error allocating %d bytes\n", strlen(FileWildcard)+1);
149 return FALSE;
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;
160 break;
162 else
164 if (tmpWildcard[0] == '*' && tmpWildcard[1] != '.')
166 PrintWildcardError();
167 return FALSE;
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]);
179 WildcardCount = 0;
181 else if (*tmpWildcard)
183 Wildcards[WildcardCount++] = strdup(tmpWildcard);
184 free(Wildcard);
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);
196 while (TRUE)
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);
216 if (MatchAllFiles)
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;
230 break;
232 else if (Wildcards[i][2] && IsSameExtension(FileExtension, Wildcards[i] + 2))
234 if (!AddFileToSearchResult(pDirectory, BasePathName, Filename)) return FALSE;
235 break;
238 else if (IsSameExtension(Filename, Wildcards[i]))
240 if (!AddFileToSearchResult(pDirectory, BasePathName, Filename)) return FALSE;
241 break;
245 else
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;
252 break;
254 else if (IsSameExtension(Filename, Wildcards[i])) // match file
256 if (!AddFileToSearchResult(pDirectory, BasePathName, Filename)) return FALSE;
257 break;
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());
268 return FALSE;
272 pDirectory->Finished = TRUE;
273 return 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)
284 DWORD PathLength;
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));
293 if (!pSearchResults)
295 fprintf(stderr, "Error allocating %d bytes\n", sizeof(SEARCH_RESULTS));
296 return FALSE;
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));
307 return FALSE;
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");
322 return FALSE;
325 if (!(pSearchResults->FilePath = (char *)malloc(PathLength)))
327 fprintf(stderr, "Error: unable to allocate %d bytes\n", PathLength);
328 return FALSE;
331 sprintf(pSearchResults->FilePath, "%s\\%s", BasePathName, Filename);
332 //printf("Added %s\n", pSearchResults->FilePath);
333 return TRUE;
336 BOOL MergeSearchResults(PDIRECTORY_INFO pBaseDir, PDIRECTORY_INFO pSubDir)
338 DWORD PathLength;
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));
349 if (!pDestination)
351 fprintf(stderr, "Error allocating %d bytes\n", sizeof(SEARCH_RESULTS));
352 return FALSE;
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));
366 return FALSE;
369 pDestination->Next->Previous = pDestination;
370 pDestination = pDestination->Next;
371 pDestination->Next = NULL;
374 while (TRUE)
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);
381 return FALSE;
384 strcpy(pDestination->FilePath, pSource->FilePath);
385 //printf("Merged %s\n", pDestination->FilePath);
387 pSource = pSource->Next;
388 if (!pSource) break;
390 pDestination->Next = malloc(sizeof(SEARCH_RESULTS));
391 if (!pDestination->Next)
393 fprintf(stderr, "Error: unable to allocate %d bytes\n", sizeof(SEARCH_RESULTS));
394 return FALSE;
397 pDestination->Next->Previous = pDestination;
398 pDestination = pDestination->Next;
399 pDestination->Next = NULL;
402 //printf("New base directory contains:\n");
403 //DumpSearchResults(pSubDir->pSearchResults);
404 return TRUE;
407 DWORD GetSearchResultCount(PSEARCH_RESULTS pSearchResult)
409 DWORD Count = 0;
411 if (!pSearchResult) return 0;
412 while (pSearchResult && pSearchResult->Next)
414 pSearchResult = pSearchResult->Next;
415 Count++;
418 return Count + 1;
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);