// Windows/FileFind.cpp #include "StdAfx.h" #include "FileFind.h" #ifndef _UNICODE #include "../Common/StringConvert.h" #endif #include #include #include #ifdef HAVE_LSTAT extern int global_use_lstat; int global_use_lstat=1; // default behaviour : p7zip stores symlinks instead of dumping the files they point to #endif #define NEED_NAME_WINDOWS_TO_UNIX #include "myPrivate.h" // #define TRACEN(u) u; #define TRACEN(u) /* */ void my_windows_split_path(const AString &p_path, AString &dir , AString &base) { int pos = p_path.ReverseFind('/'); if (pos == -1) { // no separator dir = "."; if (p_path.IsEmpty()) base = "."; else base = p_path; } else if ((pos+1) < p_path.Length()) { // true separator base = p_path.Mid(pos+1); while ((pos >= 1) && (p_path[pos-1] == '/')) pos--; if (pos == 0) dir = "/"; else dir = p_path.Left(pos); } else { // separator at the end of the path // pos = p_path.find_last_not_of("/"); pos = -1; int ind = 0; while (p_path[ind]) { if (p_path[ind] != '/') pos = ind; ind++; } if (pos == -1) { base = "/"; dir = "/"; } else { my_windows_split_path(p_path.Left(pos+1),dir,base); } } } static void my_windows_split_path(const UString &p_path, UString &dir , UString &base) { int pos = p_path.ReverseFind(L'/'); if (pos == -1) { // no separator dir = L"."; if (p_path.IsEmpty()) base = L"."; else base = p_path; } else if ((pos+1) < p_path.Length()) { // true separator base = p_path.Mid(pos+1); while ((pos >= 1) && (p_path[pos-1] == L'/')) pos--; if (pos == 0) dir = L"/"; else dir = p_path.Left(pos); } else { // separator at the end of the path // pos = p_path.find_last_not_of("/"); pos = -1; int ind = 0; while (p_path[ind]) { if (p_path[ind] != L'/') pos = ind; ind++; } if (pos == -1) { base = L"/"; dir = L"/"; } else { my_windows_split_path(p_path.Left(pos+1),dir,base); } } } static int filter_pattern(const char *string , const char *pattern , int flags_nocase) { if ((string == 0) || (*string==0)) { if (pattern == 0) return 1; while (*pattern=='*') ++pattern; return (!*pattern); } switch (*pattern) { case '*': if (!filter_pattern(string+1,pattern,flags_nocase)) return filter_pattern(string,pattern+1,flags_nocase); return 1; case 0: if (*string==0) return 1; break; case '?': return filter_pattern(string+1,pattern+1,flags_nocase); default: if ( ((flags_nocase) && (tolower(*pattern)==tolower(*string))) || (*pattern == *string) ) { return filter_pattern(string+1,pattern+1,flags_nocase); } break; } return 0; } namespace NWindows { namespace NFile { namespace NFind { static const TCHAR kDot = TEXT('.'); bool CFileInfo::IsDots() const { if (!IsDirectory() || Name.IsEmpty()) return false; if (Name[0] != kDot) return false; return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); } #ifndef _UNICODE bool CFileInfoW::IsDots() const { if (!IsDirectory() || Name.IsEmpty()) return false; if (Name[0] != kDot) return false; return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); } #endif static bool originalFilename(const UString & src, AString & res) { // Try to recover the original filename res = ""; int is_good = 1; int i=0; while (src[i]) { if (src[i] >= 256) { return false; } else { res += char(src[i]); } i++; } return true; } // Warning this function cannot update "fileInfo.Name" static int fillin_CFileInfo(CFileInfo &fileInfo,const char *filename) { struct stat stat_info; int ret; #ifdef HAVE_LSTAT if (global_use_lstat) { ret = lstat(filename,&stat_info); } else #endif { ret = stat(filename,&stat_info); } if (ret != 0) return ret; /* FIXME : FILE_ATTRIBUTE_HIDDEN ? */ if (S_ISDIR(stat_info.st_mode)) { fileInfo.Attributes = FILE_ATTRIBUTE_DIRECTORY; } else { fileInfo.Attributes = FILE_ATTRIBUTE_ARCHIVE; } if (!(stat_info.st_mode & S_IWUSR)) fileInfo.Attributes |= FILE_ATTRIBUTE_READONLY; fileInfo.Attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((stat_info.st_mode & 0xFFFF) << 16); RtlSecondsSince1970ToFileTime( stat_info.st_ctime, &fileInfo.CreationTime ); RtlSecondsSince1970ToFileTime( stat_info.st_mtime, &fileInfo.LastWriteTime ); RtlSecondsSince1970ToFileTime( stat_info.st_atime, &fileInfo.LastAccessTime ); if (S_ISDIR(stat_info.st_mode)) { fileInfo.Size = 0; } else { // file or symbolic link fileInfo.Size = stat_info.st_size; // for a symbolic link, size = size of filename } return 0; } static int fillin_CFileInfo(CFileInfo &fileInfo,const char *dir,const char *name) { struct stat stat_info; char filename[MAX_PATHNAME_LEN]; size_t dir_len = strlen(dir); size_t name_len = strlen(name); size_t total = dir_len + 1 + name_len; // 1 = strlen("/"); if (total >= MAX_PATHNAME_LEN) throw "fillin_CFileInfo - internal error - MAX_PATHNAME_LEN"; memcpy(filename,dir,dir_len); if (dir_len >= 1) { if (filename[dir_len-1] == CHAR_PATH_SEPARATOR) { // delete the '/' dir_len--; } } filename[dir_len] = CHAR_PATH_SEPARATOR; memcpy(filename+(dir_len+1),name,name_len+1); // copy also final '\0' fileInfo.Name = name; int ret = fillin_CFileInfo(fileInfo,filename); if (ret != 0) { AString err_msg = "stat error for "; err_msg += filename; err_msg += " ("; err_msg += strerror(errno); err_msg += ")"; throw err_msg; } return ret; } //////////////////////////////// // CFindFile bool CFindFile::Close() { if(_dirp == 0) return true; int ret = closedir(_dirp); if (ret == 0) { _dirp = 0; return true; } return false; } bool CFindFile::FindFirst(LPCTSTR wildcard, CFileInfo &fileInfo) { Close(); if ((!wildcard) || (wildcard[0]==0)) { SetLastError(ERROR_PATH_NOT_FOUND); return false; } my_windows_split_path(nameWindowToUnix(wildcard),_directory,_pattern); TRACEN((printf("CFindFile::FindFirst : %s (dirname=%s,pattern=%s)\n",wildcard,(const char *)_directory,(const char *)_pattern))) _dirp = ::opendir((const char *)_directory); TRACEN((printf("CFindFile::FindFirst : opendir=%p\n",_dirp))) if ((_dirp == 0) && (global_use_utf16_conversion)) { // Try to recover the original filename UString ustr = MultiByteToUnicodeString(_directory, 0); AString resultString; bool is_good = originalFilename(ustr, resultString); if (is_good) { _dirp = ::opendir((const char *)resultString); _directory = resultString; } } if (_dirp == 0) return false; struct dirent *dp; while ((dp = readdir(_dirp)) != NULL) { if (filter_pattern(dp->d_name,(const char *)_pattern,0) == 1) { int retf = fillin_CFileInfo(fileInfo,(const char *)_directory,dp->d_name); if (retf) { TRACEN((printf("CFindFile::FindFirst : closedir-1(dirp=%p)\n",_dirp))) closedir(_dirp); _dirp = 0; SetLastError( ERROR_NO_MORE_FILES ); return false; } TRACEN((printf("CFindFile::FindFirst -%s- true\n",dp->d_name))) return true; } } TRACEN((printf("CFindFile::FindFirst : closedir-2(dirp=%p)\n",_dirp))) closedir(_dirp); _dirp = 0; SetLastError( ERROR_NO_MORE_FILES ); return false; } bool CFindFile::FindFirst(LPCWSTR wildcard, CFileInfoW &fileInfo) { Close(); CFileInfo fileInfo0; AString Awildcard = UnicodeStringToMultiByte(wildcard, CP_ACP); bool bret = FindFirst((LPCTSTR)Awildcard, fileInfo0); if (bret) { fileInfo.Attributes = fileInfo0.Attributes; fileInfo.CreationTime = fileInfo0.CreationTime; fileInfo.LastAccessTime = fileInfo0.LastAccessTime; fileInfo.LastWriteTime = fileInfo0.LastWriteTime; fileInfo.Size = fileInfo0.Size; fileInfo.Name = GetUnicodeString(fileInfo0.Name, CP_ACP); } return bret; } bool CFindFile::FindNext(CFileInfo &fileInfo) { if (_dirp == 0) { SetLastError( ERROR_INVALID_HANDLE ); return false; } struct dirent *dp; while ((dp = readdir(_dirp)) != NULL) { if (filter_pattern(dp->d_name,(const char *)_pattern,0) == 1) { int retf = fillin_CFileInfo(fileInfo,(const char *)_directory,dp->d_name); if (retf) { TRACEN((printf("FindNextFileA -%s- ret_handle=FALSE (errno=%d)\n",dp->d_name,errno))) return false; } TRACEN((printf("FindNextFileA -%s- true\n",dp->d_name))) return true; } } TRACEN((printf("FindNextFileA ret_handle=FALSE (ERROR_NO_MORE_FILES)\n"))) SetLastError( ERROR_NO_MORE_FILES ); return false; } bool CFindFile::FindNext(CFileInfoW &fileInfo) { CFileInfo fileInfo0; bool bret = FindNext(fileInfo0); if (bret) { fileInfo.Attributes = fileInfo0.Attributes; fileInfo.CreationTime = fileInfo0.CreationTime; fileInfo.LastAccessTime = fileInfo0.LastAccessTime; fileInfo.LastWriteTime = fileInfo0.LastWriteTime; fileInfo.Size = fileInfo0.Size; fileInfo.Name = GetUnicodeString(fileInfo0.Name, CP_ACP); } return bret; } bool FindFile(LPCTSTR wildcard, CFileInfo &fileInfo) { // CFindFile finder; // return finder.FindFirst(wildcard, fileInfo); AString dir,base; my_windows_split_path(wildcard, dir , base); int ret = fillin_CFileInfo(fileInfo,nameWindowToUnix(wildcard)); fileInfo.Name = base; TRACEN((printf("FindFile(%s,CFileInfo) ret=%d\n",wildcard,ret))) return (ret == 0); } #ifndef _UNICODE bool FindFile(LPCWSTR wildcard, CFileInfoW &fileInfo) { // CFindFile finder; // return finder.FindFirst(wildcard, fileInfo); AString name = UnicodeStringToMultiByte(wildcard, CP_ACP); CFileInfo fileInfo0; int ret = fillin_CFileInfo(fileInfo0,nameWindowToUnix((const char *)name)); TRACEN((printf("FindFile-1(%s,CFileInfo) ret=%d\n",(const char *)name,ret))) if (ret != 0) { // Try to recover the original filename AString resultString; bool is_good = originalFilename(wildcard, resultString); if (is_good) { ret = fillin_CFileInfo(fileInfo0,nameWindowToUnix((const char *)resultString)); TRACEN((printf("FindFile-2(%s,CFileInfo) ret=%d\n",(const char *)resultString,ret))) } } if (ret == 0) { UString dir,base; my_windows_split_path(wildcard, dir , base); fileInfo.Attributes = fileInfo0.Attributes; fileInfo.CreationTime = fileInfo0.CreationTime; fileInfo.LastAccessTime = fileInfo0.LastAccessTime; fileInfo.LastWriteTime = fileInfo0.LastWriteTime; fileInfo.Size = fileInfo0.Size; fileInfo.Name = base; } return (ret == 0); } #endif bool DoesFileExist(LPCTSTR name) { CFileInfo fileInfo; int ret = fillin_CFileInfo(fileInfo,nameWindowToUnix(name)); TRACEN((printf("DoesFileExist(%s) ret=%d\n",name,ret))) return (ret == 0); } #ifndef _UNICODE bool DoesFileExist(LPCWSTR name) { AString Aname = UnicodeStringToMultiByte(name, CP_ACP); bool bret = DoesFileExist((LPCTSTR)Aname); if (bret) return bret; // Try to recover the original filename AString resultString; bool is_good = originalFilename(name, resultString); if (is_good) { bret = DoesFileExist((const char *)resultString); } return bret; } #endif ///////////////////////////////////// // CEnumerator bool CEnumerator::NextAny(CFileInfo &fileInfo) { if(_findFile.IsHandleAllocated()) return _findFile.FindNext(fileInfo); else return _findFile.FindFirst(_wildcard, fileInfo); } bool CEnumerator::Next(CFileInfo &fileInfo) { while(true) { if(!NextAny(fileInfo)) return false; if(!fileInfo.IsDots()) return true; } } bool CEnumerator::Next(CFileInfo &fileInfo, bool &found) { if (Next(fileInfo)) { found = true; return true; } found = false; return (::GetLastError() == ERROR_NO_MORE_FILES); } #ifndef _UNICODE bool CEnumeratorW::NextAny(CFileInfoW &fileInfo) { if(_findFile.IsHandleAllocated()) return _findFile.FindNext(fileInfo); else return _findFile.FindFirst(_wildcard, fileInfo); } bool CEnumeratorW::Next(CFileInfoW &fileInfo) { while(true) { if(!NextAny(fileInfo)) return false; if(!fileInfo.IsDots()) return true; } } bool CEnumeratorW::Next(CFileInfoW &fileInfo, bool &found) { if (Next(fileInfo)) { found = true; return true; } found = false; return (::GetLastError() == ERROR_NO_MORE_FILES); } #endif }}}