#ifndef FILEENTRIES_HPP #define FILEENTRIES_HPP /* Part of Underdog. https://underdog.sourceforge.net * Released under GPLv3. Other licenses may be available. * Robert White © Copyright 2014 */ #include /* Defines DT_* constants */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FileWrapper.h" namespace FileEntries { using namespace ::boost; using namespace ::boost::multi_index; enum FileType { UnknownFile, BlockFile, CharFile, DirectoryFile, PipeFile, SymlinkFile, SocketFile, RegularFile }; inline enum FileType DecodeFileType(const int filetype) { enum FileType retval = UnknownFile; switch (filetype) { case DT_BLK: retval = BlockFile; break; case DT_CHR: retval = CharFile; break; case DT_DIR: retval = DirectoryFile; break; case DT_FIFO: retval = PipeFile; break; case DT_LNK: retval = SymlinkFile; break; case DT_REG: retval = RegularFile; break; case DT_SOCK: retval = BlockFile; break; } return retval; } struct FileEntry { struct NameIndex {}; struct DirectoryIndex {}; struct INodeIndex {}; struct TypedIndex {}; dev_t Device; long ParentINode; long INode; std::string Name; enum FileType Type; }; typedef multi_index_container< FileEntry, indexed_by< ordered_non_unique< tag, member< FileEntry, std::string, &FileEntry::Name > >, ordered_non_unique< tag, composite_key< FileEntry, member< FileEntry, dev_t, &FileEntry::Device >, member< FileEntry, long, &FileEntry::ParentINode > > >, ordered_non_unique< tag, composite_key< FileEntry, member< FileEntry, dev_t, &FileEntry::Device >, member< FileEntry, long, &FileEntry::INode > > >, ordered_non_unique< tag, composite_key< FileEntry, member< FileEntry, enum FileType, &FileEntry::Type >, member< FileEntry, std::string, &FileEntry::Name > > > > > FileSet; struct regex { regex_t built; regex(const std::string pattern): built({}) { if (regcomp(&built,pattern.c_str(),REG_EXTENDED|REG_NOSUB) != 0) { throw std::exception(); } } ~regex() { regfree(&built); } bool operator()(const std::string & value) const { return 0 == regexec(&built,value.c_str(),0,0,0); } }; struct selector { typedef ::std::pair result_type; }; struct shallow_selector: public selector { regex include_pattern; shallow_selector(const std::string pattern = ::std::string("^(([^.])|([.][^.])|([.]{2}.))")): include_pattern(pattern) { return; } result_type operator()(const FileEntry & ent) const { return result_type(include_pattern(ent.Name),false); } }; struct deep_selector: public selector { regex include_pattern; regex recurse_pattern; deep_selector( const std::string i_pattern = "^(([^.])|([.][^.])|([.]{2}.))", const std::string r_pattern = "^(([^.])|([.][^.])|([.]{2}.))" ): include_pattern(i_pattern), recurse_pattern(r_pattern) { return; } result_type operator()(const FileEntry & ent) const { bool candidate = (ent.Type == DirectoryFile) || (ent.Type == SymlinkFile); return result_type(include_pattern(ent.Name),candidate && recurse_pattern(ent.Name)); } }; template < class U, bool First = true, bool Second = false > struct inverted: public selector { const U & Real; inverted(const U & real): Real(real) { return; } result_type operator()(const FileEntry & ent) const { result_type retval = Real(ent); if (First) { retval.first = !retval.first; } if (Second) { retval.second = !retval.second; } return retval; } }; struct linux_dirent { long d_inode; off_t d_offset; unsigned short d_reclen; char d_name[1]; }; union cursor_type { char * ch; struct linux_dirent * ld; }; template void Fill(int directory_fd, FileSet & result, const U & selector ) { FileEntry temp = {}; int count; char buffer[1024]; char* cursor; char* extent; struct stat directory_stat = {}; fstat(directory_fd,&directory_stat); FileSet::index::type & result_view = result.get(); temp.Device = directory_stat.st_dev; temp.ParentINode = directory_stat.st_ino; result_view.erase( result_view.lower_bound(boost::make_tuple(temp.Device,temp.ParentINode)), result_view.upper_bound(boost::make_tuple(temp.Device,temp.ParentINode)) ); lseek(directory_fd,0,SEEK_SET); while ((count = syscall(SYS_getdents,directory_fd,buffer,sizeof(buffer))) > 0) { int next = 0; extent = buffer + count; for (cursor = buffer; cursor < extent; cursor += next) { linux_dirent * entry = reinterpret_cast(cursor); next = entry->d_reclen; temp.INode = entry->d_inode; temp.Name = entry->d_name; temp.Type = DecodeFileType(static_cast(*(cursor + next - 1))); ::std::pair selection = selector(temp); if (selection.first) { result_view.insert(temp); } if (selection.second) try { FileWrapper subdir(temp.Name,O_DIRECTORY|O_RDONLY,directory_fd); Fill(subdir.fd(),result,selector); } catch (...) { // No recursion on error, silently, it's just easier. } } } return; } template <> void Fill(int directory_fd, FileSet & result, const std::string & selector ) { return Fill(directory_fd,result,shallow_selector(selector)); } inline void Fill(int directory_fd, FileSet & result ) { return Fill(directory_fd,result,shallow_selector()); } } #endif