/** * @file FindFile.h * @brief ワイルドカード指定してファイル一覧を取得. 再帰指定可能. * @author tenk* (Masashi Kitamura) * @note * - win または linux(unix) 用. * - fname.h を先にincludeしてから、このファイルをincludeすれば、 * fname側のルーチンを用いる. そうでなければ交替関数をこのクラス内に用意. * - Public Domain Software */ #ifndef FINDFILE_H_INCLUDED #define FINDFILE_H_INCLUDED #if defined _WIN32 #pragma once #include <windows.h> #include <tchar.h> #if defined _MSC_VER #pragma comment(lib, "User32.lib") // CharNext()で必要... #pragma warning(disable: 4244) #endif #else // グローバル空間を汚すが、同一マクロの再定義は大丈夫だろうとしてtchar関係の辻褄あわせ. #define _LARGEFILE64_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <dirent.h> #include <sys/stat.h> #include <fnmatch.h> #ifndef TCHAR #define TCHAR char #define _T(x) x #endif #endif #include <assert.h> #include <stddef.h> #include <time.h> // パスの長さを設定. #ifdef FINDFILE_MAX_PATH // 設定済みならそれを利用. #elif defined FNAME_MAX_PATH // fname.h がinclude済みならそれに従う. #define FINDFILE_MAX_PATH FNAME_MAX_PATH #elif defined _MAX_PATH // dos/win系の定義. #define FINDFILE_MAX_PATH _MAX_PATH #else // なければ 適当. #define FINDFILE_MAX_PATH 1024 #endif // ---------------------------------------------------------------------------- /// ファイル検索用クラス. template<unsigned N = FINDFILE_MAX_PATH > class FindFileT { public: FindFileT() : recFlag_(0), srchLen_(0) { pathBuf_[0] = 0, srchName_[0] = 0; } template<class FNC> FindFileT(const TCHAR* srchName, unsigned flags, FNC& fnc) { scan(srchName, flags, fnc); } template<class FNC> unsigned scan(const TCHAR* srchName, unsigned flags, FNC fnc); private: template<class FNC> unsigned scan_sub(FNC& fnc); #ifndef FNAME_INCLUDED TCHAR* fname_baseName(const TCHAR* path); TCHAR* fname_cpy(TCHAR dst[], unsigned sz, const TCHAR* src); #endif private: TCHAR pathBuf_ [ N ]; TCHAR srchName_[ N ]; unsigned srchLen_; bool recFlag_; bool hiddonFlag_; }; /** ファイル検索. */ template<unsigned N> template<class FNC> unsigned FindFileT<N>::scan(const TCHAR* srchName, unsigned flags, FNC fnc) { fname_cpy(pathBuf_ , N, srchName ); TCHAR* baseName = fname_baseName(pathBuf_); fname_cpy(srchName_, N, baseName ); bool recFlag = (flags & 1) != 0; #if 1 // "**" の指定を再帰指定とみなす. { TCHAR* p = srchName_; TCHAR* d = srchName_; while (*p) { if (*p == '*' && p[1] == '*') { recFlag = 1; ++p; } *d++ = *p++; } *d = 0; } #endif recFlag_ = recFlag; hiddonFlag_ = (flags & 2) != 0; #ifdef _WIN32 srchLen_ = _tcslen(srchName_); #else srchLen_ = strlen(srchName_); if (baseName == pathBuf_) { fname_cpy(pathBuf_, N, _T("./")); } #endif //func_ = fnc; return scan_sub(fnc); } /** ファイル検索のサブルーチン. 再帰する. */ template<unsigned N> template<class FNC> unsigned FindFileT<N>::scan_sub(FNC& fnc) { #if defined _WIN32 unsigned num = 0; WIN32_FIND_DATA findData = {0}; WIN32_FIND_DATA* pFindData = &findData; // new WIN32_FIND_DATA; HANDLE hdl = FindFirstFile(pathBuf_, pFindData); size_t baseNameSz = N - _tcslen(pathBuf_); TCHAR* baseName = fname_baseName(pathBuf_); *baseName = _T('\0'); if (hdl != INVALID_HANDLE_VALUE) { // ファイル名を取得. ※ 隠しファイルは対象外にしておく. do { fname_cpy(baseName, baseNameSz, pFindData->cFileName ); if ((pFindData->dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN)) == 0) { fnc( pathBuf_, pFindData ); ++num; } } while (FindNextFile(hdl, pFindData) != 0); FindClose(hdl); } // ディレクトリ再帰でファイル名を取得. if (recFlag_ && baseNameSz >= 16) { fname_cpy(baseName, 4, _T("*.*") ); hdl = FindFirstFile(pathBuf_, pFindData); if (hdl != INVALID_HANDLE_VALUE) { do { fname_cpy(baseName, baseNameSz, pFindData->cFileName ); if ((pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (_tcscmp(baseName, _T(".")) == 0 || _tcscmp(baseName, _T("..")) == 0 || (pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) // 隠しフォルダは対象外. { ; } else { size_t l = _tcslen(baseName); if (l+2+srchLen_ < baseNameSz) { fname_cpy(baseName+l , baseNameSz-l , _T("/")); fname_cpy(baseName+l+1, baseNameSz-l-1, srchName_ ); num += scan_sub(fnc); } } } } while (FindNextFile(hdl, pFindData) != 0); FindClose(hdl); } } // delete pFindData; return num; #else // linux/unix系... struct dirent** namelist = 0; unsigned num = 0; int dirNum; TCHAR* baseName; size_t baseNameSz; baseName = fname_baseName(pathBuf_); *baseName = 0; baseNameSz = N - strlen(pathBuf_); assert(baseNameSz < FINDFILE_MAX_PATH); // ディレクトリエントリの取得. baseName[-1] = 0; dirNum = scandir(pathBuf_, &namelist, 0, alphasort); baseName[-1] = _T('/'); if (namelist) { struct stat statBuf; int i; // ファイル名を取得. for (i = 0; i < dirNum; ++i) { struct dirent* d = namelist[i]; if (fnmatch(srchName_, d->d_name, FNM_NOESCAPE) == 0) { fname_cpy(baseName, baseNameSz, d->d_name ); if (stat(pathBuf_, &statBuf) >= 0) { if ((statBuf.st_mode & S_IFMT) != S_IFDIR) { fnc( pathBuf_ , &statBuf ); ++num; } } } } // ディレクトリがあれば再帰. if (recFlag_ && baseNameSz >= 16) { for (i = 0; i < dirNum; ++i) { struct dirent* d = namelist[i]; fname_cpy(baseName, baseNameSz, d->d_name ); if (stat(pathBuf_, &statBuf) >= 0 && strcmp(baseName,_T(".")) && strcmp(baseName,_T(".."))) { if ((statBuf.st_mode & S_IFMT) == S_IFDIR) { size_t l = strlen(baseName); if (l+2+srchLen_ < baseNameSz) { fname_cpy(baseName+l , baseNameSz-l , _T("/")); fname_cpy(baseName+l+1, baseNameSz-l-1, srchName_ ); num += scan_sub(fnc); } } } } } // 使ったメモリを開放. for (i = 0; i < dirNum; ++i) { free( namelist[i] ); } free( namelist ); } return num; #endif } // ---------------------------------------------------------------------------- // ファイル名関係. #ifndef FNAME_INCLUDED /** ファイルパス名中のディレクトリを除いたファイル名の位置を返す. */ template<unsigned N> TCHAR *FindFileT<N>::fname_baseName(const TCHAR *adr) { const TCHAR *p = adr; assert(adr != 0); while (*p) { #ifdef _WIN32 if (*p == ':' || *p == '/'|| *p == '\\') adr = (TCHAR*)p + 1; p = CharNext(p); #else if (*p == ':' || *p == '/' ) adr = (TCHAR*)p + 1; p += 1; #endif } return (TCHAR*)adr; } /** ファイル名のコピー. */ template<unsigned N> TCHAR *FindFileT<N>::fname_cpy(TCHAR dst[], unsigned dstSz, const TCHAR* src) { const TCHAR* s = src; const TCHAR* e = s + dstSz-1; TCHAR* d = dst; while (s < e && *s) *d++ = *s++; *d = 0; return dst; } #endif // ---------------------------------------------------------------------------- // 検索対象のデータを受け取るためのクラス. /// ファイル名のみの一覧作成用. template<class CONTA> struct FindFile_Get_Name { public: typedef typename CONTA::value_type value_type; FindFile_Get_Name(CONTA& rConta) : rConta_(rConta) {} void operator()(const TCHAR* path, const void*) { rConta_.push_back( value_type(path) ); } private: //FindFile_Get_Name(const FindFile_Get_Name&); void operator=(const FindFile_Get_Name&); private: CONTA& rConta_; }; /// ファイル名とサイズの一覧作成用. template<class CONTA> struct FindFile_Get_NameSize { public: typedef typename CONTA::value_type value_type; FindFile_Get_NameSize(CONTA& rConta) : rConta_(rConta) {} #ifdef _WIN32 void operator()(const TCHAR* path, const WIN32_FIND_DATA* f) { rConta_.push_back( value_type(path, (((unsigned __int64)f->nFileSizeHigh<<32)|f->nFileSizeLow) ) ); } #else void operator()(const TCHAR* path, const struct stat* st) { rConta_.push_back( value_type(path, st->st_size) ); } #endif private: void operator=(const FindFile_Get_NameSize&); private: CONTA& rConta_; }; /// ファイル名とサイズと最終書き込み時間の一覧作成用. template<class CONTA> struct FindFile_Get_NameSizeTime { public: typedef typename CONTA::value_type value_type; FindFile_Get_NameSizeTime(CONTA& rConta) : rConta_(rConta) {} #ifdef _WIN32 void operator()(const TCHAR* path, const WIN32_FIND_DATA* f) { unsigned __int64 sz = (((unsigned __int64)f->nFileSizeHigh<<32) | f->nFileSizeLow); unsigned __int64 wrtTim = *(unsigned __int64*)&f->ftLastWriteTime; rConta_.push_back( value_type(path, sz, wrtTim ) ); } #else void operator()(const TCHAR* path, const struct stat *st) { rConta_.push_back( value_type(path, st->st_size, st->st_mtime) ); } #endif private: void operator=(const FindFile_Get_NameSizeTime&); private: CONTA& rConta_; }; /// ファイル名とstruct stat 情報の一覧作成用. template<class CONTA> struct FindFile_Get_NameStat { public: typedef typename CONTA::value_type value_type; FindFile_Get_NameStat(CONTA& rConta) : rConta_(rConta) {} #ifdef _WIN32 void operator()(const TCHAR* path, const WIN32_FIND_DATA* f) { struct _tstat64 st; ::_tstat64(path, &st); rConta_.push_back( value_type(path, &st ) ); } #else void operator()(const TCHAR* path, const struct stat *st) { rConta_.push_back( value_type(path, st) ); } #endif private: void operator=(const FindFile_Get_NameStat&); private: CONTA& rConta_; }; // ---------------------------------------------------------------------------- // 実際に呼び出す関数. /** ファイル名の一覧作成. * コンテナの要素は 文字列を受け取れること. * @param rConta 結果を受け取るコンテナ. push_back()可能なこと. * @param srchPath 検索パス指定 * @param flags bit0(1):再帰するならon指定. bit1(2):隠しファイルも検索するならon指定. */ template<class CONTA > unsigned FindFile_name(CONTA& rConta, const TCHAR* srchPath, unsigned flags=0) { return FindFileT<>().scan( srchPath, flags, FindFile_Get_Name<CONTA>(rConta) ); } /** ファイル名とサイズの一覧作成. * コンテナの要素は 文字列とサイズを受け取れるクラスであること. * @param rConta 結果を受け取るコンテナ. push_back()可能なこと. * @param srchPath 検索パス指定 * @param flags bit0(1):再帰するならon指定. bit1(2):隠しファイルも検索するならon指定. */ template<class CONTA > unsigned FindFile_nameSize(CONTA& rConta, const TCHAR* srchPath, unsigned flags=0) { return FindFileT<>().scan( srchPath, flags, FindFile_Get_NameSize< CONTA >(rConta) ); } /** ファイル名とサイズと最終書き込み時間の一覧作成. * コンテナの要素は 文字列とサイズと時間を受け取れるクラスであること. * @param rConta 結果を受け取るコンテナ. push_back()可能なこと. * @param srchPath 検索パス指定 * @param flags bit0(1):再帰するならon指定. bit1(2):隠しファイルも検索するならon指定. */ template<class CONTA > unsigned FindFile_nameSizeTime(CONTA& rConta, const TCHAR* srchPath, unsigned flags=0) { return FindFileT<>().scan( srchPath, flags, FindFile_Get_NameSizeTime< CONTA >(rConta) ); } #if 0 // FindFile_nameSizeTime 用のクラス例. /// ファイル名とサイズと最終書き込み時間の構造体. struct findfile_name_size_time_t { findfile_name_size_time_t( const TCHAR* name=_T(""), uint64_t size=0, uint64_t time=0 ) : name_(name), size_(size), time_(time) { } #ifdef UNICODE std::wstring name_; #else std::string name_; #endif uint64_t size_; uint64_t time_; }; typedef std::vector< findfile_name_size_time_t > fildFile_name_size_time_vec_t; typedef std::list< findfile_name_size_time_t > fildFile_name_size_time_list_t; #endif /** ファイル名とstruct _stat64(win) / stat(win以外) の一覧作成. * コンテナの要素は 文字列とstat(64)ポインタサイズを受け取れるクラスであること. * @param rConta 結果を受け取るコンテナ. push_back()可能なこと. * @param srchPath 検索パス指定 * @param flags bit0(1):再帰するならon指定. bit1(2):隠しファイルも検索するならon指定. */ template<class CONTA > unsigned FindFile_nameStat(CONTA& rConta, const TCHAR* srchPath, unsigned flags=0) { return FindFileT<>().scan( srchPath, flags, FindFile_Get_NameStat< CONTA >(rConta) ); } #endif // FINDFILE_H_INCLUDED