const char* fileglob_FileName(fileglob* self) { #if defined(WIN32) if (self->context->handle != INVALID_HANDLE_VALUE) { SplicePath(&self->combinedName, buffer_ptr(&self->context->basePath), self->context->fd.cFileName); return buffer_ptr(&self->combinedName); } #else if (self->context->dirp) { SplicePath(&self->combinedName, buffer_ptr(&self->context->basePath), self->context->dp->d_name); return buffer_ptr(&self->combinedName); } else { return buffer_ptr(&self->context->patternBuf); } #endif return NULL; }
//-------------------------------------------------------------------------------- // Decide how a particular pattern should be handled, and call function for each. //-------------------------------------------------------------------------------- void MyGlob(const char * Pattern , void (*FileFuncParm)(const char * FileName)) { char BasePattern[_MAX_PATH]; char MatchPattern[_MAX_PATH]; char PatCopy[_MAX_PATH*2+1]; int a; int MatchFiles, MatchDirs; int BaseEnd, PatternEnd; int SawPat; int RecurseAt; strcpy(PatCopy, Pattern); #ifdef DEBUGGING printf("Called with '%s'\n",Pattern); #endif DoRecursion: MatchFiles = FALSE; MatchDirs = TRUE; BaseEnd = 0; PatternEnd = 0; SawPat = FALSE; RecurseAt = -1; // Split the path into base path and pattern to match against using findfirst. for (a=0;;a++){ if (PatCopy[a] == '*' || PatCopy[a] == '?'){ SawPat = TRUE; } if (PatCopy[a] == '*' && PatCopy[a+1] == '*'){ if (a == 0 || PatCopy[a-1] == '\\' || PatCopy[a-1] == ':'){ if (PatCopy[a+2] == '\\' || PatCopy[a+2] == '\0'){ // x\**\y ---> x\y x\*\**\y RecurseAt = a; if (PatCopy[a+2]){ memcpy(PatCopy+a, PatCopy+a+3, strlen(PatCopy)-a-1); }else{ PatCopy[a+1] = '\0'; } } } } if (PatCopy[a] == '\\' || (PatCopy[a] == ':' && PatCopy[a+1] != '\\')){ PatternEnd = a; if (SawPat) break; // Findfirst can only match one level of wildcard at a time. BaseEnd = a+1; } if (PatCopy[a] == '\0'){ PatternEnd = a; MatchFiles = TRUE; MatchDirs = FALSE; break; } } if (!SawPat){ // No pattern. This should refer to a file. FileFuncParm(PatCopy); return; } strncpy(BasePattern, PatCopy, BaseEnd); BasePattern[BaseEnd] = 0; strncpy(MatchPattern, PatCopy, PatternEnd); MatchPattern[PatternEnd] = 0; #ifdef DEBUGGING printf("Base:%s Pattern:%s Files:%d dirs:%d\n",BasePattern, MatchPattern, MatchFiles, MatchDirs); #endif { FileEntry * FileList = NULL; int NumAllocated = 0; int NumHave = 0; struct _finddata_t finddata; long find_handle; find_handle = _findfirst(MatchPattern, &finddata); for (;;){ if (find_handle == -1) break; // Eliminate the obvious patterns. if (!memcmp(finddata.name, ".",2)) goto next_file; if (!memcmp(finddata.name, "..",3)) goto next_file; if (finddata.attrib & _A_SUBDIR){ if (!MatchDirs) goto next_file; }else{ if (!MatchFiles) goto next_file; } // Add it to the list. if (NumAllocated <= NumHave){ NumAllocated = NumAllocated+10+NumAllocated/2; FileList = realloc(FileList, NumAllocated * sizeof(FileEntry)); if (FileList == NULL) goto nomem; } a = strlen(finddata.name); FileList[NumHave].Name = malloc(a+1); if (FileList[NumHave].Name == NULL){ nomem: printf("malloc failure\n"); exit(-1); } memcpy(FileList[NumHave].Name, finddata.name, a+1); FileList[NumHave].attrib = finddata.attrib; NumHave++; next_file: if (_findnext(find_handle, &finddata) != 0) break; } _findclose(find_handle); // Sort the list... qsort(FileList, NumHave, sizeof(FileEntry), CompareFunc); // Use the list. for (a=0;a<NumHave;a++){ char CombinedName[_MAX_PATH*2+1]; if (FileList[a].attrib & _A_SUBDIR){ if (MatchDirs){ // Need more directories. SplicePath(CombinedName, BasePattern, FileList[a].Name); strncat(CombinedName, PatCopy+PatternEnd, _MAX_PATH*2-strlen(CombinedName)); MyGlob(CombinedName,FileFuncParm); } }else{ if (MatchFiles){ // We need files at this level. SplicePath(CombinedName, BasePattern, FileList[a].Name); FileFuncParm(CombinedName); } } free(FileList[a].Name); } free(FileList); } if(RecurseAt >= 0){ strcpy(MatchPattern, PatCopy+RecurseAt); PatCopy[RecurseAt] = 0; strncpy(PatCopy+RecurseAt, "*\\**\\", _MAX_PATH*2-RecurseAt); strncat(PatCopy, MatchPattern, _MAX_PATH*2-strlen(PatCopy)); #ifdef DEBUGGING printf("Recurse with '%s'\n",PatCopy); #endif // As this function context is no longer needed, we can just goto back // to the top of it to avoid adding another context on the stack. goto DoRecursion; } }
int _fileglob_GlobHelper(fileglob* self, const char* inPattern) { fileglob_Context* context = self->context; int hasWildcard; int found; Setup: if (!context) { context = (fileglob_Context*)self->allocFunction(self->userData, NULL, sizeof(fileglob_Context)); context->prev = self->context; #if defined(WIN32) context->handle = INVALID_HANDLE_VALUE; #else context->dirp = NULL; context->hasattr = 0; context->statted = 0; #endif context->pattern = NULL; context->iterNode = NULL; context->directoryListHead = context->directoryListTail = 0; context->basePathLastSlashPos = 0; buffer_initwithalloc(&context->patternBuf, self->allocFunction, self->userData); buffer_addstring(&context->patternBuf, inPattern, strlen(inPattern) + 1); buffer_initwithalloc(&context->basePath, self->allocFunction, self->userData); buffer_initwithalloc(&context->matchPattern, self->allocFunction, self->userData); self->context = context; if (context->prev == NULL) return 1; } DoRecursion: found = 1; if (!context->pattern) { char* pattern; context->basePathEndPos = context->basePathLastSlashPos = 0; context->recurseAtPos = (size_t)-1; // Split the path into base path and pattern to match against. hasWildcard = 0; for (pattern = buffer_ptr(&context->patternBuf); *pattern != '\0'; ++pattern) { char ch = *pattern; // Is it a '?' ? if (ch == '?') hasWildcard = 1; // Is it a '*' ? else if (ch == '*') { hasWildcard = 1; // Is there a '**'? if (pattern[1] == '*') { // If we're just starting the pattern or the characters immediately // preceding the pattern are a drive letter ':' or a directory path // '/', then set up the internals for later recursion. if (pattern == buffer_ptr(&context->patternBuf) || pattern[-1] == '/' || pattern[-1] == ':') { char ch2 = pattern[2]; if (ch2 == '/') { context->recurseAtPos = pattern - buffer_ptr(&context->patternBuf); memcpy(pattern, pattern + 3, strlen(pattern) - 2); buffer_deltapos(&context->patternBuf, -3); } else if (ch2 == '\0') { context->recurseAtPos = pattern - buffer_ptr(&context->patternBuf); *pattern = '\0'; } } } } // Is there a '/' or ':' in the pattern at this location? if (ch == '/' || ch == ':') { if (hasWildcard) break; else { if (pattern[1]) context->basePathLastSlashPos = pattern - buffer_ptr(&context->patternBuf) + 1; context->basePathEndPos = pattern - buffer_ptr(&context->patternBuf) + 1; } } } context->pattern = pattern; // Copy the directory down. context->basePathLen = context->basePathEndPos; buffer_reset(&context->basePath); buffer_addstring(&context->basePath, buffer_ptr(&context->patternBuf), context->basePathLen); buffer_addchar(&context->basePath, 0); if (context->iterNode) { context->matchFiles = *context->pattern == 0; goto NextDirectory; } } #if defined(WIN32) if (context->handle == INVALID_HANDLE_VALUE) { #else if (!context->dirp && !context->statted) { #endif size_t matchLen; // Did we make it to the end of the pattern? If so, we should match files, // since there were no slashes encountered. context->matchFiles = *context->pattern == 0; // Copy the wildcard matching string. matchLen = (context->pattern - buffer_ptr(&context->patternBuf)) - context->basePathLen; buffer_reset(&context->matchPattern); buffer_addstring(&context->matchPattern, buffer_ptr(&context->patternBuf) + context->basePathLen, matchLen + 1); buffer_deltapos(&context->matchPattern, -1); if (*buffer_posptr(&context->matchPattern) == '/') { buffer_deltapos(&context->matchPattern, 1); buffer_addchar(&context->matchPattern, 0); } #if defined(WIN32) // Do the file search with *.* in the directory specified in basePattern. buffer_setpos(&context->basePath, context->basePathEndPos); buffer_addstring(&context->basePath, "*.*", 4); // Start the find. context->handle = FindFirstFile(buffer_ptr(&context->basePath), &context->fd); if (context->handle == INVALID_HANDLE_VALUE) { found = 0; } #else // Start the find. buffer_setpos(&context->basePath, context->basePathEndPos); buffer_addchar(&context->basePath, 0); context->dirp = opendir(buffer_ptr(&context->basePath)[0] ? buffer_ptr(&context->basePath) : "."); if (!context->dirp) { found = 0; } else { context->dp = readdir(context->dirp); found = context->dp != NULL; } #endif // Clear out the *.* so we can use the original basePattern string. buffer_setpos(&context->basePath, context->basePathEndPos); buffer_putchar(&context->basePath, 0); } else { goto NextFile; } // Any files found? #if defined(WIN32) if (context->handle != INVALID_HANDLE_VALUE) { #else if (context->dirp) { #endif for (;;) { #if defined(WIN32) char* filename = context->fd.cFileName; #else char* filename = context->dp->d_name; context->hasattr = 0; #endif // Is the file a directory? #if defined(WIN32) if (context->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { #else if (context->dp->d_type == DT_DIR) { #endif // Knock out "." or ".." int ignore = filename[0] == '.' && (filename[1] == 0 || (filename[1] == '.' && filename[2] == 0)); // Should this file be ignored? int matches = 0; if (!ignore) { size_t len = strlen(filename); filename[len] = '/'; filename[len + 1] = '\0'; matches = fileglob_WildMatch(buffer_ptr(&context->matchPattern), filename, 0); } // Do a wildcard match. if (!ignore && matches) { // It matched. Let's see if the file should be ignored. // See if this is a directory to ignore. ignore = _fileglob_MatchIgnoreDirectoryPattern(self, filename); // Should this file be ignored? if (!ignore) { _fileglob_list_append(self, &context->directoryListHead, &context->directoryListTail, filename); // Is this pattern exclusive? if (self->exclusiveDirectoryPatternsHead) { if (_fileglob_MatchExclusiveDirectoryPattern(self, filename)) break; } else { if ((!context->matchFiles && context->pattern[0] == '/' && context->pattern[1] == 0) || (self->filesAndFolders)) break; } } } } else { // Do a wildcard match. if (fileglob_WildMatch(buffer_ptr(&context->matchPattern), filename, 0)) { // It matched. Let's see if the file should be ignored. int ignore = _fileglob_MatchIgnoreFilePattern(self, filename); // Is this pattern exclusive? if (!ignore && self->exclusiveFilePatternsHead) { ignore = !_fileglob_MatchExclusiveFilePattern(self, filename); } // Should this file be ignored? if (!ignore) { if (context->matchFiles) break; } } } NextFile: // Look up the next file. #if defined(WIN32) found = FindNextFile(context->handle, &context->fd) == TRUE; #else if (context->dirp) { context->dp = readdir(context->dirp); found = context->dp != NULL; } else { found = 0; } #endif if (!found) break; } if (!found) { // Close down the file find handle. #if defined(WIN32) FindClose(context->handle); context->handle = INVALID_HANDLE_VALUE; #else if (context->dirp) { closedir(context->dirp); context->dirp = NULL; } #endif context->iterNode = context->directoryListHead; } } // Iterate the file list and either recurse or add the file as a found // file. if (!context->matchFiles) { if (found) { return 1; } NextDirectory: if (context->iterNode) { // Need more directories. SplicePath(&self->combinedName, buffer_ptr(&context->basePath), context->iterNode->buffer); buffer_deltapos(&self->combinedName, -2); buffer_addstring(&self->combinedName, context->pattern, strlen(context->pattern) + 1); context->iterNode = context->iterNode->next; context = NULL; inPattern = buffer_ptr(&self->combinedName); goto Setup; } } else { if (found) return 1; } // Do we need to recurse? if (context->recurseAtPos == (size_t)-1) { _fileglob_FreeContextLevel(self); context = self->context; if (!context) return 0; goto NextDirectory; } buffer_reset(&context->matchPattern); buffer_setpos(&context->patternBuf, context->recurseAtPos); buffer_addstring(&context->matchPattern, buffer_posptr(&context->patternBuf), strlen(buffer_posptr(&context->patternBuf))); buffer_addstring(&context->patternBuf, "*/**/", 5); buffer_addstring(&context->patternBuf, buffer_ptr(&context->matchPattern), buffer_pos(&context->matchPattern) + 1); inPattern = buffer_ptr(&context->patternBuf); context->pattern = NULL; if (context->matchFiles) { context->iterNode = context->directoryListHead; } else { _fileglob_list_clear(self, &context->directoryListHead, &context->directoryListTail); } goto DoRecursion; }