public import "ecere" public import "FileSystemIterator" public class FileSystemCacheIterator : InterfaceFileSystemIterator { public: FileSystemCache cache; bool Iterate(char * startPath, bool followLinks) { bool result = true; char * path; FSCacheObject o; if(cache && cache.objects.count) { o = cache.FetchObject(startPath); if(o) { bool listDirEntries; FileSystemCacheIteratorStackFrame frame = null; Array stack { }; if(iterateStartPath) { path = o.GetPath(); listDirEntries = OnObject(owner, o.name, path, o.stats, true); if(o.firstChild) { if(listDirEntries) { OnEnteringDirectory(owner, path); stack.Add((frame = { path, o.firstChild })); path = null; } else result = false; } delete path; } // else { /* TODO: frame is not initialized!! */ } while(frame) { o = frame.o; if(o) { frame.o = o.nextSibling; path = o.GetPath(); listDirEntries = OnObject(owner, o.name, path, o.stats, !iterateStartPath && stack.count == 1); if(o.firstChild) { if(listDirEntries) { OnEnteringDirectory(owner, path); stack.Add((frame = { path, o.firstChild })); path = null; } else result = false; } delete path; } else { if(stack.count > (iterateStartPath ? 0 : 1)) OnLeavingDirectory(owner, frame.path); delete frame; stack.lastIterator.Remove(); frame = stack.count == 0 ? null : stack.lastIterator.data; } } if(stack.count != 0) PrintLn("dddddddd"); delete stack; } } return result; } } class FileSystemCacheIteratorStackFrame : struct { public: String path; FSCacheObject o; private: ~FileSystemCacheIteratorStackFrame() { delete path; } }; class FileSystemCacheBits { bool indexInodes:1; } public class DummyFileSystemCacheWindow : Window { public: FileSystemCache cache { }; } public class FileSystemCache { public: Map roots { }; Array objects { }; Array normalParentStack; Map deviceCaches { }; property bool indexInodes { set { bits.indexInodes = value; } get { return bits.indexInodes; } }; void/*FileSystemCache*/ ::Cache(char * path, bool indexInodes, DummyFileSystemCacheWindow dummyWindow) { FileSystemCache cache = dummyWindow.cache; // TOIMP: for now we will assume the cache includes files from a single domain (partition/share) // TOIMP: would like to use full blown file iterator driver system to support other types of file system, archives, etc FileAttribs pathAttribs = FileExists(path); if(pathAttribs) { cache = dummyWindow.cache;// = { }; cache.indexInodes = indexInodes; cache.normalParentStack = { }; //cache.domainParentStack = { }; if(pathAttribs.isDirectory) { FileSystemIterator fsi { owner = dummyWindow; //owner = (void *)this; iterateStartPath = true; // COMPILER TOFIX: this will not compile (error: method class must be derived from class) -- bool Whatever/*FileSystemCache*/::OnFolder(...) bool /*FileSystemCache*/DummyFileSystemCacheWindow::OnObject(const char * name, const char * path, FileStats stats, bool isRootObject) { bool result = true; FileSystemCache cache = this.cache; //uint dev = stats.dev; //uint inode = stats.inode; char * p; FSCacheObject parent = isRootObject ? null : cache.normalParentStack.lastIterator.data; FSCacheObject o { parent, name = CopyString(isRootObject ? path : name), stats = stats }; //FileStats s; if(isRootObject) { // TODO see if we can find a parent for it // see if we already have a FSCacheObject for it, if so, update // distant parents -- when there are gaps.. maybe the parent's parent is present but not the parent // keep a list of those and see if they can be pluged in after a Cache run cache.roots[o] = FSCacheRoot { cache.objects.count, GetPathDirNamesArray(path) }; } else { if(!parent.firstChild) parent.firstChild = parent.lastChild = o; else { parent.lastChild.nextSibling = o; o.prevSibling = parent.lastChild; parent.lastChild = o; } } cache.objects.Add(o); #if _DEBUG /* if(cache.objects.count % 1000 == 0) PrintLn(path, " ----- ", cache.objects.count / 1000, " --------- ", dev); */ //FileGetStatsLink(path, s); /* if(s.inode != inode) { PrintLn(s.inode); PrintLn(inode); } */ p = o.GetPath(); if(strcmp(p, path)) { int c; PrintLn(p); for(c = 0; c < cache.normalParentStack.count; c++) { delete p; p = cache.normalParentStack[c].GetPath(); PrintLn(p); } PrintLn(path); } delete p; #endif /* if(dev && inode && cache.bits.indexInodes) { FileSystemDeviceCache devCache = cache.deviceCaches[dev]; if(!devCache) cache.deviceCaches[dev] = devCache = { }; { Array inodes = devCache.inodes[inode]; if(!inodes) devCache.inodes[inode] = inodes = { }; else if(inodes.count == 1) devCache.nonSingleLinks[inode] = true; inodes.Add(o); } } */ return result; } void /*FileSystemCache*/DummyFileSystemCacheWindow::OnEnteringDirectory(const char * path) { FileSystemCache cache = this.cache; FSCacheObject o = cache.objects.lastIterator.data; cache.normalParentStack.Add(o); } void /*FileSystemCache*/DummyFileSystemCacheWindow::OnLeavingDirectory(const char * path) { FileSystemCache cache = this.cache; cache.normalParentStack.lastIterator.Remove(); } }; fsi.Iterate(path, false); { if(cache.objects.count) { for(dev : cache.deviceCaches) { PrintLn("# of inodes (dev", &dev, "): ", dev.inodes.count); if(dev.nonSingleLinks.count) { for(inode : dev.nonSingleLinks) { PrintLn(" ", &inode); for(o : dev.inodes[&inode]) { char * path = o.GetPath(); PrintLn(path); delete path; } } } } } } delete cache.normalParentStack; delete fsi; } } //return cache; } FSCacheObject FetchObject(char * path) { FSCacheObject result = null; for(root : roots) { FSCacheObject o = &root; Array names = GetPathDirNamesArray(path); int c, max = Min(names.count, root.names.count); for(c = 0; c < max; c++) { if(strcmp(names[c], root.names[c])) break; } if(c == max) { if(c == names.count) result = o; else { for(; c < names.count; c++) { FSCacheObject child = o.firstChild; o = null; while(child) { if(!strcmp(child.name, names[c])) { o = child; break; } child = child.nextSibling; } if(!o) break; } if(o) result = o; } break; } delete names; } return result; } bool FileGetStats(char * path, FileStats stats) { FSCacheObject o = FetchObject(path); if(o) stats = o.stats; else stats = { }; return o != null; } Map> devsInodesDone { }; Map linksPaths { }; void Special(char * path/*, Map linksPaths*//*Map> devsInodesDone*/, DummyFileSystemCacheWindow dummyWindow) { FileSystemCacheIterator fsci { owner = dummyWindow, cache = this, iterateStartPath = true; bool /*FileSystemCache*/DummyFileSystemCacheWindow::OnObject(const char * name, const char * path, FileStats stats, bool isRootObject) { /* uint dev = stats.dev; uint inode = stats.inode; FileSystemCache cache = this.cache; if(dev && inode) { FileSystemDeviceCache devCache = cache.deviceCaches[dev]; if(devCache) { Array links = devCache.inodes[inode]; if(links && links.count) { if(links.count > 1) { Map inodesDone = cache.devsInodesDone[dev]; Map linksPaths = cache.linksPaths; bool done; if(!inodesDone) cache.devsInodesDone[dev] = inodesDone = { }; done = inodesDone[inode]; if(!done) { inodesDone[inode] = true; //PrintLn(" ", &inode); for(o : links) { char * path = o.GetPath(); //PrintLn(path); //delete path; linksPaths[path] = true; } } } } else PrintLn("no links/count!!"); } else PrintLn("no devCache!!"); } else PrintLn("no dev/inode"); */ return true; } }; fsci.Iterate(path, false); delete fsci; } void SpecialPrint() { MapNode mn; PrintLn(" -------------------------------------------- Special -------------------------------------------- "); PrintLn(" ------------------------------------------------------------------------------------------------- "); for(mn = linksPaths.root.minimum; mn; mn = mn.next) { PrintLn(mn.key); } } private: FileSystemCacheBits bits; ~FileSystemCache() { objects.Free(); delete objects; } } public class FileSystemDeviceCache : struct { public: Map> inodes { }; Map nonSingleLinks { }; // using a map presuming it will be faster than Array::Find() //Array nonSingleLinks { }; private: } class FSCacheRoot : struct { public: uint position; Array names; private: ~FSCacheRoot() { if(names) { names.Free(); delete names; } } } /* enum FSCacheObjectType { normal }; class FSCacheObjectBits { FSCacheObjectType type:4; bool isRoot:1; } */ public class FSCacheObject : struct { public: FSCacheObject parent; char * name; FileStats stats; FSCacheObject firstChild; FSCacheObject lastChild; FSCacheObject prevSibling; FSCacheObject nextSibling; char * GetPath() { int c; char path[MAX_LOCATION] = ""; FSCacheObject o; Array stack { }; for(o = this; o; o = o.parent) stack.Add(o); for(c = stack.count - 1; c >= 0; c--) { o = stack[c]; if(c < stack.count - 1) strcat(path, DIR_SEPS); strcat(path, o.name); } delete stack; return CopyString(path); } private: // FSCacheObjectBits bits; ~FSCacheObject() { delete name; } } Array GetPathDirNamesArray(const char * path) { Array names; if(path && path[0]) { const char * p = path; char rest[MAX_LOCATION]; char name[MAX_FILENAME]; names = { }; while(strlen(p)) { SplitDirectory(p, name, rest); if(p == path) p = rest; names.Add(CopyString(name)); } } return names; }