Exemple #1
0
 unsafe static extern FTSENT *fts_set(IntPtr fts_handle, FTSENT *ent, int options);
Exemple #2
0
        unsafe static IList <DirectoryEntry> Scan(string path, bool recurse, Predicate <string> cb_shouldskipdir)
        {
            if (recurse)
            {
                bool   first = true;
                IntPtr str   = Marshal.StringToHGlobalAnsi(path);
                try
                {
                    IntPtr[] arr = new IntPtr[] { str, IntPtr.Zero };
                    IntPtr   handle;

                    fixed(IntPtr *arg = arr)
                    handle = fts_open(new IntPtr(arg), FTS_NOCHDIR | FTS_PHYSICAL, IntPtr.Zero);

                    if (handle == IntPtr.Zero)
                    {
                        throw new Exception("failed to fts_open");
                    }

                    try
                    {
                        FTSENT *ent = fts_read(handle);
                        if (ent == null)
                        {
                            return(__empty);
                        }

                        List <DirectoryEntry> ret = new List <DirectoryEntry>();
                        while (ent != null)
                        {
                            //Console.WriteLine("ent: " + ent->ToString());
                            bool isfile;
                            if (ent->fts_info == FTS_F)
                            {
                                isfile = true;
                            }
                            else if (ent->fts_info == FTS_D)
                            {
                                isfile = false;
                            }
                            else
                            {
                                ent = fts_read(handle);
                                continue;
                            }
                            bool   skip     = false;
                            string fullpath = Marshal.PtrToStringAnsi(ent->fts_path);
                            if (!isfile && cb_shouldskipdir != null)
                            {
                                if (cb_shouldskipdir != null && cb_shouldskipdir(fullpath))
                                {
                                    fts_set(handle, ent, FTS_SKIP);
                                    skip = true;
                                }
                            }
                            if (!skip)
                            {
                                if (first)
                                {
                                    first = false;
                                }
                                else
                                {
                                    long mtime = (long)ent->fts_statp->st_mtimespec.tv_sec;
                                    ret.Add(new DirectoryEntry(fullpath, mtime, isfile));
                                }
                            }
                            ent = fts_read(handle);
                        }
                        return(ret);
                    }
                    finally
                    {
                        fts_close(handle);
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(str);
                }
            }
            else
            {
#if !HAVE_MONO_POSIX
                // I've verified that this returns the exact same timestamps as the other branch of code
                // on linux + mac. In theory, this should perform just as well, but unsure. We can optimize
                // further later with p/invoke if necessary
                var           ret  = new List <DirectoryEntry>();
                DirectoryInfo info = new DirectoryInfo(path);
                foreach (var entry in info.EnumerateFileSystemInfos())
                {
                    var attrs = entry.Attributes & ~(FileAttributes.Hidden | FileAttributes.ReadOnly);
                    if ((attrs & FileAttributes.Directory) != 0)
                    {
                        ret.Add(new DirectoryEntry(path, entry.FullName, 0, false));
                    }
                    else
                    {
                        ret.Add(new DirectoryEntry(path, entry.FullName, (entry.LastWriteTimeUtc.Ticks - EPOCH_TICKS) / 10000000, true));
                    }
                }
                return(ret);
#else
                var ret = new List <DirectoryEntry>();

                IntPtr dirp = Syscall.opendir(path);
                if (dirp == IntPtr.Zero)
                {
                    throw new Exception("opendir: error " + Stdlib.GetLastError());
                }

                try
                {
                    do
                    {
                        Dirent d = Syscall.readdir(dirp);
                        if (d == null)
                        {
                            Errno rc = Stdlib.GetLastError();
                            if (rc == Errno.ENOENT || rc == 0)
                            {
                                break;                                // done
                            }
                            throw new Exception("readdir: error " + rc);
                        }

                        if (d.d_type == READDIR_ISFILE)
                        {
                            Stat   st;
                            string fn = Path.Combine(path, d.d_name);
                            if (Syscall.stat(fn, out st) != 0)
                            {
                                throw new Exception("stat \"" + fn + "\": error " + Stdlib.GetLastError());
                            }

                            ret.Add(new DirectoryEntry(path, d.d_name, st.st_mtime, true));
                        }
                        else if (d.d_type == READDIR_ISDIRECTORY)
                        {
                            if (d.d_name == "." || d.d_name == "..")
                            {
                                continue;
                            }
                            ret.Add(new DirectoryEntry(path, d.d_name, 0, false));
                        }
                    }while (dirp != IntPtr.Zero);
                }
                finally
                {
                    Syscall.closedir(dirp);
                }

                return(ret);
#endif
            }
        }