--- /dev/null
+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<FileSystemCacheIteratorStackFrame> 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<FSCacheObject, FSCacheRoot> roots { };
+ Array<FSCacheObject> objects { };
+ Array<FSCacheObject> normalParentStack;
+ Map<uint, FileSystemDeviceCache> 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(char * name, 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);
+ PrintLn("crap");
+ }
+ p = o.GetPath();
+ if(strcmp(p, path))
+ {
+ int c;
+ PrintLn("damn");
+ PrintLn(p);
+ for(c = 0; c < cache.normalParentStack.count; c++)
+ {
+ delete p;
+ p = cache.normalParentStack[c].GetPath();
+ PrintLn(p);
+ }
+ PrintLn(path);
+ PrintLn("bad");
+ }
+ delete p;
+#endif
+ if(dev && inode && cache.bits.indexInodes)
+ {
+ FileSystemDeviceCache devCache = cache.deviceCaches[dev];
+ if(!devCache)
+ cache.deviceCaches[dev] = devCache = { };
+ {
+ Array<FSCacheObject> 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(char * path)
+ {
+ FileSystemCache cache = this.cache;
+ FSCacheObject o = cache.objects.lastIterator.data;
+ cache.normalParentStack.Add(o);
+ }
+
+ void /*FileSystemCache*/DummyFileSystemCacheWindow::OnLeavingDirectory(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<String> 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<uint, Map<uint, bool>> devsInodesDone { };
+ Map<String, bool> linksPaths { };
+ void Special(char * path/*, Map<String, bool> linksPaths*//*Map<uint, Map<uint, bool>> devsInodesDone*/, DummyFileSystemCacheWindow dummyWindow)
+ {
+ FileSystemCacheIterator fsci { owner = dummyWindow, cache = this, iterateStartPath = true;
+ bool /*FileSystemCache*/DummyFileSystemCacheWindow::OnObject(char * name, 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<FSCacheObject> links = devCache.inodes[inode];
+ if(links && links.count)
+ {
+ if(links.count > 1)
+ {
+ Map<uint, bool> inodesDone = cache.devsInodesDone[dev];
+ Map<String, bool> 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<String, bool> 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<uint, Array<FSCacheObject>> inodes { };
+ Map<uint, bool> nonSingleLinks { }; // using a map presuming it will be faster than Array::Find() //Array<uint> nonSingleLinks { };
+
+private:
+}
+
+class FSCacheRoot : struct
+{
+public:
+ uint position;
+ Array<String> 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<FSCacheObject> 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<String> GetPathDirNamesArray(char * path)
+{
+ Array<String> names;
+ if(path && path[0])
+ {
+ 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;
+}