/// \file /// \brief Support to find files /// /// /// filesearch: /// /// ATTENTION : make sure there is enouth space in filename to put a full path (255 or 512) /// if needmd5check == 0 there is no md5 check /// if completepath then filename will be change with the full path and name /// maxsearchdepth == 0 only search given directory, no subdirs /// return FS_NOTFOUND /// FS_MD5SUMBAD; /// FS_FOUND #include #ifdef __GNUC__ #include #endif #if defined (_WIN32) && !defined (_XBOX) //#define WIN32_LEAN_AND_MEAN #define RPC_NO_WINDOWS_H #include #endif #ifdef _WIN32_WCE #include "sdl/SRB2CE/cehelp.h" #else #include #endif #include #include "filesrch.h" #include "d_netfil.h" #include "m_misc.h" #if (defined (_WIN32) && !defined (_WIN32_WCE)) && defined (_MSC_VER) && !defined (_XBOX) #include #include #include #define SUFFIX "*" #define SLASH "\\" #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif struct dirent { long d_ino; /* Always zero. */ unsigned short d_reclen; /* Always zero. */ unsigned short d_namlen; /* Length of name in d_name. */ char d_name[FILENAME_MAX]; /* File name. */ }; /* * This is an internal data structure. Good programmers will not use it * except as an argument to one of the functions below. * dd_stat field is now int (was short in older versions). */ typedef struct { /* disk transfer area for this dir */ struct _finddata_t dd_dta; /* dirent struct to return from dir (NOTE: this makes this thread * safe as long as only one thread uses a particular DIR struct at * a time) */ struct dirent dd_dir; /* _findnext handle */ #if _MSC_VER > 1200 intptr_t dd_handle; #else long dd_handle; #endif /* * Status of search: * 0 = not started yet (next entry to read is first entry) * -1 = off the end * positive = 0 based index of next entry */ int dd_stat; /* given path for dir with search pattern (struct is extended) */ CHAR dd_name[1]; } DIR; /* * opendir * * Returns a pointer to a DIR structure appropriately filled in to begin * searching a directory. */ DIR * opendir (const CHAR *szPath) { DIR *nd; DWORD rc; CHAR szFullPath[MAX_PATH]; errno = 0; if (!szPath) { errno = EFAULT; return (DIR *) 0; } if (szPath[0] == '\0') { errno = ENOTDIR; return (DIR *) 0; } /* Attempt to determine if the given path really is a directory. */ rc = GetFileAttributesA(szPath); if (rc == INVALID_FILE_ATTRIBUTES) { /* call GetLastError for more error info */ errno = ENOENT; return (DIR *) 0; } if (!(rc & FILE_ATTRIBUTE_DIRECTORY)) { /* Error, entry exists but not a directory. */ errno = ENOTDIR; return (DIR *) 0; } /* Make an absolute pathname. */ _fullpath (szFullPath, szPath, MAX_PATH); /* Allocate enough space to store DIR structure and the complete * directory path given. */ nd = (DIR *) malloc (sizeof (DIR) + (strlen(szFullPath) + strlen (SLASH) + strlen(SUFFIX) + 1) * sizeof (CHAR)); if (!nd) { /* Error, out of memory. */ errno = ENOMEM; return (DIR *) 0; } /* Create the search expression. */ strcpy (nd->dd_name, szFullPath); /* Add on a slash if the path does not end with one. */ if (nd->dd_name[0] != '\0' && nd->dd_name[strlen (nd->dd_name) - 1] != '/' && nd->dd_name[strlen (nd->dd_name) - 1] != '\\') { strcat (nd->dd_name, SLASH); } /* Add on the search pattern */ strcat (nd->dd_name, SUFFIX); /* Initialize handle to -1 so that a premature closedir doesn't try * to call _findclose on it. */ nd->dd_handle = -1; /* Initialize the status. */ nd->dd_stat = 0; /* Initialize the dirent structure. ino and reclen are invalid under * Win32, and name simply points at the appropriate part of the * findfirst_t structure. */ nd->dd_dir.d_ino = 0; nd->dd_dir.d_reclen = 0; nd->dd_dir.d_namlen = 0; ZeroMemory(nd->dd_dir.d_name, FILENAME_MAX); return nd; } /* * readdir * * Return a pointer to a dirent structure filled with the information on the * next entry in the directory. */ struct dirent * readdir (DIR * dirp) { errno = 0; /* Check for valid DIR struct. */ if (!dirp) { errno = EFAULT; return (struct dirent *) 0; } if (dirp->dd_stat < 0) { /* We have already returned all files in the directory * (or the structure has an invalid dd_stat). */ return (struct dirent *) 0; } else if (dirp->dd_stat == 0) { /* We haven't started the search yet. */ /* Start the search */ dirp->dd_handle = _findfirst (dirp->dd_name, &(dirp->dd_dta)); if (dirp->dd_handle == -1) { /* Whoops! Seems there are no files in that * directory. */ dirp->dd_stat = -1; } else { dirp->dd_stat = 1; } } else { /* Get the next search entry. */ if (_findnext (dirp->dd_handle, &(dirp->dd_dta))) { /* We are off the end or otherwise error. _findnext sets errno to ENOENT if no more file Undo this. */ DWORD winerr = GetLastError(); if (winerr == ERROR_NO_MORE_FILES) errno = 0; _findclose (dirp->dd_handle); dirp->dd_handle = -1; dirp->dd_stat = -1; } else { /* Update the status to indicate the correct * number. */ dirp->dd_stat++; } } if (dirp->dd_stat > 0) { /* Successfully got an entry. Everything about the file is * already appropriately filled in except the length of the * file name. */ dirp->dd_dir.d_namlen = (unsigned short)strlen (dirp->dd_dta.name); strcpy (dirp->dd_dir.d_name, dirp->dd_dta.name); return &dirp->dd_dir; } return (struct dirent *) 0; } /* * closedir * * Frees up resources allocated by opendir. */ int closedir (DIR * dirp) { int rc; errno = 0; rc = 0; if (!dirp) { errno = EFAULT; return -1; } if (dirp->dd_handle != -1) { rc = _findclose (dirp->dd_handle); } /* Delete the dir structure. */ free (dirp); return rc; } #endif #if defined (_XBOX) && defined (_MSC_VER) filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth) { //NONE? startpath = filename = NULL; wantedmd5sum = NULL; maxsearchdepth = 0; completepath = false; return FS_NOTFOUND; } #elif defined (_WIN32_WCE) filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth) { #ifdef __GNUC__ //NONE? startpath = filename = NULL; wantedmd5sum = NULL; maxsearchdepth = 0; completepath = false; #else WIN32_FIND_DATA dta; HANDLE searchhandle = INVALID_HANDLE_VALUE; const wchar_t wm[4] = L"*.*"; //if (startpath) SetCurrentDirectory(startpath); if (FIL_ReadFileOK(filename)) { // checkfilemd5 returns an FS_* value, either FS_FOUND or FS_MD5SUMBAD return checkfilemd5(filename, wantedmd5sum); } ZeroMemory(&dta,sizeof (dta)); if (maxsearchdepth) searchhandle = FindFirstFile(wm,&dta); if (searchhandle != INVALID_HANDLE_VALUE) { do { if ((dta.cFileName[0]!='.') && (dta.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { //if (SetCurrentDirectory(dta.cFileName)) { // can fail if we haven't the right filestatus_t found; found = filesearch(filename,NULL,wantedmd5sum,completepath,maxsearchdepth-1); //SetCurrentDirectory(".."); if (found == FS_FOUND || found == FS_MD5SUMBAD) { if (completepath) strcatbf(filename,(char *)dta.cFileName,"\\"); FindClose(searchhandle); return found; } } } } while (FindNextFile(searchhandle,&dta)==0); FindClose(searchhandle); } #endif return FS_NOTFOUND; } #else filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth) { filestatus_t retval = FS_NOTFOUND; DIR **dirhandle; struct dirent *dent; struct stat fsstat; int found = 0; char *searchname = strdup(filename); int depthleft = maxsearchdepth; char searchpath[1024]; size_t *searchpathindex; dirhandle = (DIR**) malloc(maxsearchdepth * sizeof (DIR*)); searchpathindex = (size_t *) malloc(maxsearchdepth * sizeof (size_t)); strcpy(searchpath,startpath); searchpathindex[--depthleft] = strlen(searchpath) + 1; dirhandle[depthleft] = opendir(searchpath); if (dirhandle[depthleft] == NULL) { free(searchname); free(dirhandle); free(searchpathindex); return FS_NOTFOUND; } if (searchpath[searchpathindex[depthleft]-2] != '/') { searchpath[searchpathindex[depthleft]-1] = '/'; searchpath[searchpathindex[depthleft]] = 0; } else searchpathindex[depthleft]--; while ((!found) && (depthleft < maxsearchdepth)) { searchpath[searchpathindex[depthleft]]=0; dent = readdir(dirhandle[depthleft]); if (dent) strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name); if (!dent) closedir(dirhandle[depthleft++]); else if (dent->d_name[0]=='.' && (dent->d_name[1]=='\0' || (dent->d_name[1]=='.' && dent->d_name[2]=='\0'))) { // we don't want to scan uptree } else if (stat(searchpath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat { // was the file (re)moved? can't stat it } else if (S_ISDIR(fsstat.st_mode) && depthleft) { strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name); searchpathindex[--depthleft] = strlen(searchpath) + 1; dirhandle[depthleft] = opendir(searchpath); if (!dirhandle[depthleft]) { // can't open it... maybe no read-permissions // go back to previous dir depthleft++; } searchpath[searchpathindex[depthleft]-1]='/'; searchpath[searchpathindex[depthleft]]=0; } else if (!strcasecmp(searchname, dent->d_name)) { switch (checkfilemd5(searchpath, wantedmd5sum)) { case FS_FOUND: if (completepath) strcpy(filename,searchpath); else strcpy(filename,dent->d_name); retval = FS_FOUND; found = 1; break; case FS_MD5SUMBAD: retval = FS_MD5SUMBAD; break; default: // prevent some compiler warnings break; } } } for (; depthleft < maxsearchdepth; closedir(dirhandle[depthleft++])); free(searchname); free(searchpathindex); free(dirhandle); return retval; } #endif