306 lines
8.5 KiB
C++
306 lines
8.5 KiB
C++
//--------------------------------------------------------------------------------
|
|
// Module to do recursive directory file matching under windows.
|
|
//
|
|
// Tries to do pattern matching to produce similar results as Unix, but using
|
|
// the Windows _findfirst to do all the pattern matching.
|
|
//
|
|
// Also hadles recursive directories - "**" path component expands into
|
|
// any levels of subdirectores (ie c:\**\*.c matches ALL .c files on drive c:)
|
|
//
|
|
// Matthias Wandel Nov 5 2000
|
|
//--------------------------------------------------------------------------------
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <io.h>
|
|
#include "../include/Exif/jhead.h"
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
//#define DEBUGGING
|
|
|
|
typedef struct {
|
|
char * Name;
|
|
int attrib;
|
|
}FileEntry;
|
|
|
|
|
|
#ifdef DEBUGGING
|
|
//--------------------------------------------------------------------------------
|
|
// Dummy function to show operation.
|
|
//--------------------------------------------------------------------------------
|
|
void ShowName(const char * FileName)
|
|
{
|
|
printf(" %s\n",FileName);
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Simple path splicing (assumes no '\' in either part)
|
|
//--------------------------------------------------------------------------------
|
|
static void SplicePath(char * dest, const char * p1, const char * p2)
|
|
{
|
|
int l;
|
|
l = strlen(p1);
|
|
if (!l){
|
|
strcpy(dest, p2);
|
|
}else{
|
|
if (l+strlen(p2) > _MAX_PATH-2){
|
|
fprintf(stderr,"Path too long\n");
|
|
exit(-1);
|
|
}
|
|
memcpy(dest, p1, l+1);
|
|
if (dest[l-1] != '\\' && dest[l-1] != ':'){
|
|
dest[l++] = '\\';
|
|
}
|
|
strcpy(dest+l, p2);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Qsort compare function
|
|
//--------------------------------------------------------------------------------
|
|
int CompareFunc(const void * f1, const void * f2)
|
|
{
|
|
return strcmp(((FileEntry *)f1)->Name,((FileEntry *)f2)->Name);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// 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 = (FileEntry*)realloc(FileList, NumAllocated * sizeof(FileEntry));
|
|
if (FileList == NULL) goto nomem;
|
|
}
|
|
a = strlen(finddata.name);
|
|
FileList[NumHave].Name = (char*)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;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// Flip slashes to native OS representation (for Windows)
|
|
//--------------------------------------------------------------------------------
|
|
void SlashToNative(char * Path)
|
|
{
|
|
int a;
|
|
for (a=0;Path[a];a++){
|
|
if (Path[a] == '/') Path[a] = SLASH;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUGGING
|
|
//--------------------------------------------------------------------------------
|
|
// The main program.
|
|
//--------------------------------------------------------------------------------
|
|
int main (int argc, char **argv)
|
|
{
|
|
int argn;
|
|
char * arg;
|
|
int Subdirs = 0;
|
|
|
|
for (argn=1;argn<argc;argn++){
|
|
arg = argv[argn];
|
|
if (arg[0] != '-') break; // Filenames from here on.
|
|
if (!strcmp(arg,"-r")){
|
|
printf("do recursive\n");
|
|
Subdirs = 1;
|
|
}else{
|
|
fprintf(stderr, "Argument '%s' not understood\n",arg);
|
|
}
|
|
}
|
|
if (argn == argc){
|
|
fprintf(stderr,"Error: Must supply a file name\n");
|
|
}
|
|
|
|
for (;argn<argc;argn++){
|
|
MyGlob(argv[argn], ShowName);
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
|
|
non-recursive test cases:
|
|
|
|
e:\make*\*
|
|
\make*\*
|
|
e:*\*.c
|
|
\*\*.c
|
|
\*
|
|
c:*.c
|
|
c:\*
|
|
..\*.c
|
|
|
|
|
|
recursive test cases:
|
|
**
|
|
**\*.c
|
|
c:\**\*.c
|
|
c:**\*.c
|
|
.\**
|
|
..\**
|
|
|
|
*/
|
|
|
|
|