--- file_2.040.d	2010-02-25 23:36:33.729674050 -0800
+++ file_new.d	2010-02-26 00:23:43.708673268 -0800
@@ -20,7 +20,7 @@
 
 import core.memory;
 import core.stdc.stdio, core.stdc.stdlib, core.stdc.string,
-    core.stdc.errno, std.algorithm, std.array, 
+    core.stdc.errno, std.algorithm, std.array,
     std.contracts, std.conv, std.date, std.format, std.path, std.process,
     std.range, std.regexp, std.stdio, std.string, std.traits, std.typecons,
     std.typetuple, std.utf;
@@ -105,9 +105,10 @@
             ulong st_ino64;
         }
     }
-    
+
     extern(C) int fstat64(int, struct_stat64*);
     extern(C) int stat64(in char*, struct_stat64*);
+    extern(C) int lstat64(in char*, struct_stat64*);
 }
 // }}}
 
@@ -119,14 +120,14 @@
 {
 /**
 OS error code.
- */ 
+ */
     immutable uint errno;
-    
+
 /**
 Constructor taking the name of the file where error happened and the
 error number ($(LUCKY GetLastError) in Windows, $(D getErrno) in
 Posix).
- */ 
+ */
     this(in char[] name, in char[] message)
     {
         super(text(name, ": ", message));
@@ -137,7 +138,7 @@
 Constructor taking the name of the file where error happened and the
 error number ($(LUCKY GetLastError) in Windows, $(D getErrno) in
 Posix).
- */ 
+ */
     version(Windows) this(string name, uint errno = GetLastError)
     {
         this(name, sysErrorString(errno));
@@ -185,7 +186,7 @@
     auto h = useWfuncs
         ? CreateFileW(std.utf.toUTF16z(name), defaults)
         : CreateFileA(toMBSz(name), defaults);
-    
+
     cenforce(h != INVALID_HANDLE_VALUE, name);
     scope(exit) cenforce(CloseHandle(h), name);
     auto size = GetFileSize(h, null);
@@ -193,7 +194,7 @@
     size = min(upTo, size);
     auto buf = GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size];
     scope(failure) delete buf;
-    
+
     DWORD numread = void;
     cenforce(ReadFile(h,buf.ptr, size, &numread, null) == 1
             && numread == size, name);
@@ -209,16 +210,16 @@
         sizeIncrement = 1024 * 16,
         maxSlackMemoryAllowed = 1024;
     // }
-    
+
     immutable fd = core.sys.posix.fcntl.open(toStringz(name),
             core.sys.posix.fcntl.O_RDONLY);
     cenforce(fd != -1, name);
     scope(exit) core.sys.posix.unistd.close(fd);
-    
+
     struct_stat64 statbuf = void;
     cenforce(fstat64(fd, &statbuf) == 0, name);
     //cenforce(core.sys.posix.sys.stat.fstat(fd, &statbuf) == 0, name);
-    
+
     immutable initialAlloc = statbuf.st_size
         ? min(statbuf.st_size + 1, maxInitialAlloc)
         : minInitialAlloc;
@@ -226,7 +227,7 @@
         [0 .. initialAlloc];
     scope(failure) delete result;
     size_t size = 0;
-    
+
     for (;;)
     {
         immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
@@ -239,7 +240,7 @@
         result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)
             [0 .. newAlloc];
     }
-    
+
     return result.length - size >= maxSlackMemoryAllowed
         ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
         : result[0 .. size];
@@ -249,16 +250,18 @@
 {
     write("std.file.deleteme", "1234");
     assert(read("std.file.deleteme", 2) == "12");
+    scope(exit) remove("std.file.deleteme");
 }
 
 version (linux) unittest
-{    
+{
     // A file with "zero" length that doesn't have 0 length at all
     auto s = std.file.readText("/proc/sys/kernel/osrelease");
     assert(s.length > 0);
     //writefln("'%s'", s);
 }
 
+
 /********************************************
 Read and validates (using $(XREF utf, validate)) a text file. $(D S)
 can be a type of array of characters of any width and constancy. No
@@ -308,7 +311,7 @@
     auto h = useWfuncs
         ? CreateFileW(std.utf.toUTF16z(name), defaults)
         : CreateFileA(toMBSz(name), defaults);
-   
+
     cenforce(h != INVALID_HANDLE_VALUE, name);
     scope(exit) cenforce(CloseHandle(h), name);
     DWORD numwritten;
@@ -332,11 +335,11 @@
     alias TypeTuple!(GENERIC_WRITE,0,null,OPEN_ALWAYS,
             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,HANDLE.init)
         defaults;
-    
+
     auto h = useWfuncs
         ? CreateFileW(std.utf.toUTF16z(name), defaults)
         : CreateFileA(toMBSz(name), defaults);
-    
+
     cenforce(h != INVALID_HANDLE_VALUE, name);
     scope(exit) cenforce(CloseHandle(h), name);
     DWORD numwritten;
@@ -356,12 +359,12 @@
 version(Posix) private void writeImpl(in char[] name,
         in void[] buffer, in uint mode)
 {
-    invariant fd = core.sys.posix.fcntl.open(toStringz(name),
+    immutable fd = core.sys.posix.fcntl.open(toStringz(name),
             mode, 0660);
     cenforce(fd != -1, name);
     {
         scope(failure) core.sys.posix.unistd.close(fd);
-        invariant size = buffer.length;
+        immutable size = buffer.length;
         cenforce(
             core.sys.posix.unistd.write(fd, buffer.ptr, size) == size,
             name);
@@ -418,11 +421,11 @@
     HANDLE findhndl = void;
     uint resulth = void;
     uint resultl = void;
-    
+
     if (useWfuncs)
     {
         WIN32_FIND_DATAW filefindbuf;
-        
+
         findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
         resulth = filefindbuf.nFileSizeHigh;
         resultl = filefindbuf.nFileSizeLow;
@@ -430,12 +433,12 @@
     else
     {
         WIN32_FIND_DATA filefindbuf;
-        
+
         findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
         resulth = filefindbuf.nFileSizeHigh;
         resultl = filefindbuf.nFileSizeLow;
     }
-    
+
     cenforce(findhndl != cast(HANDLE)-1 && FindClose(findhndl), name);
     return (cast(ulong) resulth << 32) + resultl;
 }
@@ -446,7 +449,7 @@
     cenforce(stat64(toStringz(name), &statbuf) == 0, name);
     return statbuf.st_size;
 }
-    
+
 unittest
 {
     version(Windows)
@@ -461,7 +464,7 @@
     write(deleteme, "abc");
     assert(getSize(deleteme) == 3);
 }
-    
+
 /*************************
  * Get creation/access/modified times of file $(D name).
  * Throws: $(D FileException) on error.
@@ -471,11 +474,11 @@
         out d_time ftc, out d_time fta, out d_time ftm)
 {
     HANDLE findhndl = void;
-    
+
     if (useWfuncs)
     {
         WIN32_FIND_DATAW filefindbuf;
-        
+
         findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
         ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime);
         fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime);
@@ -484,13 +487,13 @@
     else
     {
         WIN32_FIND_DATA filefindbuf;
-        
+
         findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
         ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime);
         fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime);
         ftm = std.date.FILETIME2d_time(&filefindbuf.ftLastWriteTime);
     }
-    
+
     if (findhndl == cast(HANDLE)-1)
     {
         throw new FileException(name.idup);
@@ -544,11 +547,11 @@
     cenforce(stat64(toStringz(name), &statbuf) == 0, name);
     return cast(d_time) statbuf.st_mtime * std.date.ticksPerSecond;
 }
-    
+
 /**
 Returns the time of the last modification of file $(D name). If the
 file does not exist, returns $(D returnIfMissing).
-   
+
 A frequent usage pattern occurs in build automation tools such as
 $(WEB gnu.org/software/make, make) or $(WEB
 en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
@@ -614,7 +617,7 @@
 {
     return access(toStringz(name), 0) == 0;
 }
-    
+
 unittest
 {
     assert(exists("."));
@@ -647,6 +650,30 @@
     return statbuf.st_mode;
 }
 
+/***************************************************
+Get file $(D name) attributes. The difference between
+$(D getLinkAttributes) and $(D getAttributes) is that
+$(D getLinkAttributes)  when $(D name) is a symbolic
+link, it returns the attributes for the link itself rather
+than the file that it points to. If $(D name) is not a
+symbolic link, then the result is the same as
+$(D getAttributes).
+
+Throws: $(D FileException) on error.
+ */
+
+version(Windows) uint getLinkAttributes(in char[] name)
+{
+    return getAttributes(name);
+}
+
+version(Posix) uint getLinkAttributes(in char[] name)
+{
+    struct_stat64 statbuf = void;
+    cenforce(lstat64(toStringz(name), &statbuf) == 0, name);
+    return statbuf.st_mode;
+}
+
 /****************************************************
  * Is $(D name) a directory?
  * Throws: $(D FileException) if $(D name) doesn't exist.
@@ -662,6 +689,16 @@
     return (getAttributes(name) & S_IFMT) == S_IFDIR;
 }
 
+version(Windows) bool isdir(uint attributes)
+{
+    return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+version(Posix) bool isdir(uint attributes)
+{
+    return (attributes & S_IFMT) == S_IFDIR;
+}
+
 /****************************************************
  * Is $(D name) a file?
  * Throws: $(D FileException) if $(D name) doesn't exist.
@@ -676,7 +713,43 @@
 {
     return (getAttributes(name) & S_IFMT) == S_IFREG;        // regular file
 }
-    
+
+version(Windows) bool isfile(uint attributes)
+{
+    return !isdir(name);
+}
+
+version(Posix) bool isfile(uint attributes)
+{
+    return (attributes & S_IFMT) == S_IFREG;        // regular file
+}
+
+/****************************************************
+ * Is $(D name) a symbolic link?
+ *
+ * Throws: $(D FileException) if $(D name) doesn't exist.
+ */
+
+version(Windows) bool issymlink(in char[] name)
+{
+    return false;
+}
+
+version(Posix) bool issymlink(in char[] name)
+{
+    return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;        // symbolic link
+}
+
+version(Windows) bool issymlink(uint linkAttributes)
+{
+    return false;
+}
+
+version(Posix) bool issymlink(uint linkAttributes)
+{
+    return (linkAttributes & S_IFMT) == S_IFLNK;        // symbolic link
+}
+
 /****************************************************
  * Change directory to $(D pathname).
  * Throws: $(D FileException) on error.
@@ -695,7 +768,7 @@
     cenforce(core.sys.posix.unistd.chdir(toStringz(pathname)) == 0,
             pathname);
 }
-    
+
 /****************************************************
 Make directory $(D pathname).
 
@@ -805,17 +878,39 @@
 
 version(Windows) struct DirEntry
 {
-    string name;                        /// file or directory name
-    ulong size = ~0UL;                        /// size of file in bytes
-    d_time creationTime = d_time_nan;        /// time of file creation
-    d_time lastAccessTime = d_time_nan;        /// time file was last accessed
-    d_time lastWriteTime = d_time_nan;        /// time file was last written to
-    uint attributes;                // Windows file attributes OR'd together
-    
-    void init(in char[] path, in WIN32_FIND_DATA *fd)
+public:
+
+    void init(in char[] path)
+    {
+        HANDLE findhndl = void;
+        uint resulth = void;
+        uint resultl = void;
+
+        if(useWfuncs)
+        {
+            WIN32_FIND_DATAW filefindbuf;
+
+            findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
+            enforce(findhndl != INVALID_HANDLE_VALUE);
+            init(path, &filefindbuf);
+        }
+        else
+        {
+            WIN32_FIND_DATA filefindbuf;
+
+            findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
+            enforce(findhndl != INVALID_HANDLE_VALUE);
+            init(path, &filefindbuf);
+        }
+
+        cenforce(findhndl != cast(HANDLE)-1 && FindClose(findhndl), name);
+        return (cast(ulong) resulth << 32) + resultl;
+    }
+
+    void init(in char[] path, in WIN32_FIND_DATA* fd)
     {
         auto clength = std.c.string.strlen(fd.cFileName.ptr);
-            
+
         // Convert cFileName[] to unicode
         const wlength = MultiByteToWideChar(0, 0, fd.cFileName.ptr,
                 clength, null,0);
@@ -824,115 +919,374 @@
                 wbuf.ptr, wlength);
         assert(n == wlength);
         // toUTF8() returns a new buffer
-        name = std.path.join(path, std.utf.toUTF8(wbuf[0 .. wlength]));
-            
-        size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
-        creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
-        lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
-        lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
-        attributes = fd.dwFileAttributes;
+        _dirname = path;
+        _basename = std.utf.toUTF8(wbuf[0 .. wlength]);
+
+        _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
+        _creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
+        _lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
+        _lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
+        _attributes = fd.dwFileAttributes;
     }
-        
+
     void init(in char[] path, in WIN32_FIND_DATAW *fd)
     {
         size_t clength = std.string.wcslen(fd.cFileName.ptr);
-        name = std.path.join(path,
-                std.utf.toUTF8(fd.cFileName[0 .. clength]));
-        size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
-        creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
-        lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
-        lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
-        attributes = fd.dwFileAttributes;
+        _dirname = path;
+        _basename = std.utf.toUTF8(fd.cFileName[0 .. clength]);
+        _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
+        _creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
+        _lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
+        _lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
+        _attributes = fd.dwFileAttributes;
+    }
+
+    /****
+     * Return base name of path for $(D DirEntry).
+     */
+    @property
+    string basename() const
+    {
+        return _basename;
+    }
+
+    /****
+     * Return directory name of path for $(D DirEntry).
+     */
+    @property
+    string dirname() const
+    {
+        return _dirname;
     }
-        
+
+    /****
+     * Return full path for $(D DirEntry).
+     */
+    @property
+    string fullname() const
+    {
+        return std.path.join(dirname, basename);
+    }
+
     /****
-     * Return $(D true) if DirEntry is a directory.
+     * Return $(D true) if $(D DirEntry) is a directory.
      */
+    @property
     bool isdir() const
     {
         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
     }
-        
+
     /****
-     * Return !=0 if DirEntry is a file.
+     * Return $(D true) if $(D DirEntry) is a regular file.
      */
+    @property
     bool isfile() const
     {
-        return !isdir;
+        //Are there no options in Windows other than directory and file?
+        //If there are, then this probably isn't the best way to determine
+        //whether this DirEntry is a file or not.
+        return !isdir();
+    }
+
+    /****
+     * Return $(D true) if $(D DirEntry) is a symbolic link.
+     */
+    @property
+    bool issymlink()
+    {
+        return false;
+    }
+
+    /****
+     * Return size of file in bytes.
+     */
+    @property
+    ulong size() const
+    {
+        return _size;
+    }
+
+    /****
+     * Return time of file creation.
+     */
+    @property
+    d_time creationTime() const
+    {
+        return _creationTime;
+    }
+
+    /****
+     * Return time file was last accessed.
+     */
+    @property
+    d_time lastAccessTime() const
+    {
+        return _lastAccessTime;
+    }
+
+    /****
+     * Return time file was last written to.
+     */
+    @property
+    d_time lastWriteTime() const
+    {
+        return _lastWriteTime;
+    }
+
+    /****
+     * Return file attributes OR'd together.
+     */
+    @property
+    uint attributes() const
+    {
+        return _attributes;
+    }
+
+    /****
+     * Return file attributes OR'd together. The difference between
+     * $(D attributes) and $(D linkAttributes) is that if the file
+     * is a symbolic link, then the attributes for the link are returned
+     * rather than the attributes for the file it points to. Otherwise,
+     * it's the same as $(D attributes).
+     */
+    @property
+    uint linkAttributes() const
+    {
+        return _attributes;
     }
+
+private:
+    string _basename; /// The file or directory name.
+    string _dirname;  /// The path to the file or directory.
+
+    d_time _creationTime = d_time_nan;   /// The time when the file was created.
+    d_time _lastAccessTime = d_time_nan; /// The time when the file was last accessed.
+    d_time _lastWriteTime = d_time_nan;  /// The time when the file was last written to.
+
+    ulong _size;       /// The size of the file in bytes.
+    uint _attributes;  /// The stat mode from stat().
 }
 
 version(Posix) struct DirEntry
 {
-    string name;                        /// file or directory name
-    ulong _size = ~0UL;                        /// size of file in bytes
-    d_time _creationTime = d_time_nan;        /// time of file creation
-    d_time _lastAccessTime = d_time_nan; /// time file was last accessed
-    d_time _lastWriteTime = d_time_nan;        /// time file was last written to
-    ubyte d_type;
-    struct_stat64 statbuf;
-    bool didstat;                        // done lazy evaluation of stat()
-        
-    void init(in char[] path, core.sys.posix.dirent.dirent *fd)
-    {
-        invariant len = std.c.string.strlen(fd.d_name.ptr);
-        name = std.path.join(path, fd.d_name[0 .. len].idup);
-        d_type = fd.d_type;
-        didstat = false;
+public:
+
+    void init(in char[] path)
+    {
+        string ipath = path.idup;
+        _dirname = std.path.dirname(ipath);
+        _basename = std.path.basename(ipath);
+
+        _didlstat = false;
+        _didstat = false;
+        _dtypeset = false;
+    }
+
+    void init(in char[] path, core.sys.posix.dirent.dirent* fd)
+    {
+        immutable len = std.c.string.strlen(fd.d_name.ptr);
+        _dirname = path.idup;
+        _basename = fd.d_name[0..len].idup;
+        _didlstat = false;
+        _didstat = false;
+
+        //fd_d_type doesn't work for all file systems,
+        //in which case the result is DT_UNKOWN. But we
+        //can determine the correct type from lstat, so
+        //we'll only set the dtype here if we could
+        //correctly determine it (not lstat in the case
+        //of DT_UNKNOWN in case we don't ever actually
+        //need the dtype, thus potentially avoiding the
+        //cost of calling lstat).
+        if(fd.d_type != DT_UNKNOWN)
+        {
+            _dType = fd.d_type;
+            _dtypeset = true;
+        }
+        else
+            _dtypeset = false;
     }
-        
-    bool isdir() const
+
+    @property
+    string basename() const
     {
-        return (d_type & DT_DIR) != 0;
+        return _basename;
     }
-        
-    bool isfile() const
+
+    @property
+    string dirname() const
+    {
+        return _dirname;
+    }
+
+    @property
+    string fullname() const
+    {
+        return std.path.join(dirname, basename);
+    }
+
+    @property
+    bool isdir()
+    {
+        _ensureStatDone();
+        return (_statMode & S_IFMT) == S_IFDIR;
+    }
+
+    @property
+    bool isfile()
     {
-        return (d_type & DT_REG) != 0;
+        _ensureStatDone();
+        return (_statMode & S_IFMT) == S_IFREG;
+    }
+
+    @property
+    bool issymlink()
+    {
+        //We can only use _dType in issymlink() because
+        //it's basically the result of lstat and isfile()
+        //and isdir() use the result of stat.
+        if(_dtypeset)
+            return _dType == DT_LNK;
+
+        _ensureLStatDone();
+        return (_lstatMode & S_IFMT) == S_IFLNK;
     }
-        
+
+    /****
+     * Return type of file per enum in $(D core.sys.posix.dirent).
+     */
+    @property
+    ubyte dtype()
+    {
+        if(!_dtypeset)
+            _ensureLStatDone();
+        return _dType;
+    }
+
+    @property
     ulong size()
     {
-        ensureStatDone;
+        _ensureStatDone();
         return _size;
     }
-        
+
+    @property
     d_time creationTime()
     {
-        ensureStatDone;
+        _ensureStatDone();
         return _creationTime;
     }
-        
+
+    @property
     d_time lastAccessTime()
     {
-        ensureStatDone;
+        _ensureStatDone();
         return _lastAccessTime;
     }
-        
+
+    @property
     d_time lastWriteTime()
     {
-        ensureStatDone;
+        _ensureStatDone();
         return _lastWriteTime;
     }
-        
+
+    @property
+    uint attributes()
+    {
+        _ensureStatDone();
+        return _statMode;
+    }
+
+    @property
+    uint linkAttributes()
+    {
+        _ensureLStatDone();
+        return _lstatMode;
+    }
+
+private:
+
     /* This is to support lazy evaluation, because doing stat's is
      * expensive and not always needed.
      */
-        
-    void ensureStatDone()
+    void _ensureStatDone()
     {
-        if (didstat) return;
-        enforce(stat64(toStringz(name), &statbuf) == 0,
-                "Failed to stat file `"~name~"'");
+        if(_didstat)
+            return;
+
+        struct_stat64 statbuf = void;
+
+        enforce(stat64(toStringz(fullname), &statbuf) == 0,
+                "Failed to stat file `"~fullname~"'");
+
+        _statMode = statbuf.st_mode;
         _size = statbuf.st_size;
-        _creationTime = cast(d_time)statbuf.st_ctime
-            * std.date.ticksPerSecond;
-        _lastAccessTime = cast(d_time)statbuf.st_atime
-            * std.date.ticksPerSecond;
-        _lastWriteTime = cast(d_time)statbuf.st_mtime
-            * std.date.ticksPerSecond;
-        didstat = true;
+        _creationTime = cast(d_time)statbuf.st_ctime *
+                                    std.date.ticksPerSecond;
+        _lastAccessTime = cast(d_time)statbuf.st_atime *
+                                      std.date.ticksPerSecond;
+        _lastWriteTime = cast(d_time)statbuf.st_mtime *
+                                     std.date.ticksPerSecond;
+        _didstat = true;
     }
+
+    /* This is to support lazy evaluation, because doing stat's is
+     * expensive and not always needed.
+     */
+    void _ensureLStatDone()
+    {
+        if(_didlstat)
+            return;
+
+        struct_stat64 statbuf = void;
+
+        enforce(lstat64(toStringz(fullname), &statbuf) == 0,
+                "Failed to stat file `"~fullname~"'");
+
+        _lstatMode = statbuf.st_mode;
+
+        if((_lstatMode & S_IFIFO) != 0)
+            _dType = DT_FIFO;
+        else if((_lstatMode & S_IFCHR) != 0)
+            _dType = DT_CHR;
+        else if((_lstatMode & S_IFDIR) != 0)
+            _dType = DT_DIR;
+        else if((_lstatMode & S_IFBLK) != 0)
+            _dType = DT_BLK;
+        else if((_lstatMode & S_IFREG) != 0)
+            _dType = DT_REG;
+        else if((_lstatMode & S_IFLNK) != 0)
+            _dType = DT_LNK;
+        else if((_lstatMode & S_IFSOCK) != 0)
+            _dType = DT_SOCK;
+        else if((_lstatMode & S_IFSOCK) != 0)
+            _dType = DT_SOCK;
+        //else if((_lstatMode & ???) != 0)
+            //_dType = DT_WHT;
+        else
+            _dType = DT_UNKNOWN;
+
+        _dtypeset = true;
+        _didlstat = true;
+    }
+
+    string _basename; /// The file or directory name.
+    string _dirname;  /// The path to the file or directory.
+
+    d_time _creationTime = d_time_nan;   /// The time when the file was created.
+    d_time _lastAccessTime = d_time_nan; /// The time when the file was last accessed.
+    d_time _lastWriteTime = d_time_nan;  /// The time when the file was last written to.
+
+    ubyte _dType;     /// The type of the file.
+    ulong _size;       /// The size of the file in bytes.
+    uint _statMode;    /// The stat mode from stat().
+    uint _lstatMode;   /// The stat mode from lstat().
+
+    bool _didlstat = false;   /// Whether lstat() has been called for this DirEntry.
+    bool _didstat = false;    /// Whether stat() has been called for this DirEntry.
+    bool _dtypeset = false;   /// Whether the dType of the file has been set.
 }
 
 /******************************************************
@@ -960,9 +1314,9 @@
  *    bool callback(DirEntry* de)
  *    {
  *      if (de.isdir)
- *        listdir(de.name, &callback);
+ *        listdir(de.fullname, &callback);
  *      else
- *        writefln(de.name);
+ *        writefln(de.fullname);
  *      return true;
  *    }
  *
@@ -979,7 +1333,7 @@
     if (useWfuncs)
     {
         WIN32_FIND_DATAW fileinfo;
-        
+
         auto h = FindFirstFileW(std.utf.toUTF16z(c), &fileinfo);
         if (h == INVALID_HANDLE_VALUE)
             return;
@@ -990,7 +1344,7 @@
             if (std.string.wcscmp(fileinfo.cFileName.ptr, ".") == 0 ||
                     std.string.wcscmp(fileinfo.cFileName.ptr, "..") == 0)
                 continue;
-            
+
             de.init(pathname, &fileinfo);
             if (!callback(&de))
                 break;
@@ -999,11 +1353,11 @@
     else
     {
         WIN32_FIND_DATA fileinfo;
-        
+
         auto h = FindFirstFileA(toMBSz(c), &fileinfo);
         if (h == INVALID_HANDLE_VALUE)
             return;
-        
+
         scope(exit) FindClose(h);
         do
         {
@@ -1011,7 +1365,7 @@
             if (std.c.string.strcmp(fileinfo.cFileName.ptr, ".") == 0 ||
                     std.c.string.strcmp(fileinfo.cFileName.ptr, "..") == 0)
                 continue;
-            
+
             de.init(pathname, &fileinfo);
             if (!callback(&de))
                 break;
@@ -1043,7 +1397,7 @@
 
 version(Windows) void copy(in char[] from, in char[] to)
 {
-    invariant result = useWfuncs
+    immutable result = useWfuncs
         ? CopyFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to), false)
         : CopyFileA(toMBSz(from), toMBSz(to), false);
     if (!result)
@@ -1052,16 +1406,16 @@
 
 version(Posix) void copy(in char[] from, in char[] to)
 {
-    invariant fd = core.sys.posix.fcntl.open(toStringz(from), O_RDONLY);
+    immutable fd = core.sys.posix.fcntl.open(toStringz(from), O_RDONLY);
     cenforce(fd != -1, from);
     scope(exit) core.sys.posix.unistd.close(fd);
-        
+
     struct_stat64 statbuf = void;
     cenforce(fstat64(fd, &statbuf) == 0, from);
     //cenforce(core.sys.posix.sys.stat.fstat(fd, &statbuf) == 0, from);
-        
+
     auto toz = toStringz(to);
-    invariant fdw = core.sys.posix.fcntl.open(toz,
+    immutable fdw = core.sys.posix.fcntl.open(toz,
             O_CREAT | O_WRONLY | O_TRUNC, 0660);
     cenforce(fdw != -1, from);
     scope(failure) std.c.stdio.remove(toz);
@@ -1075,10 +1429,10 @@
             buf = enforce(std.c.stdlib.malloc(BUFSIZ), "Out of memory");
         }
         scope(exit) std.c.stdlib.free(buf);
-            
+
         for (auto size = statbuf.st_size; size; )
         {
-            invariant toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
+            immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
             cenforce(
                 core.sys.posix.unistd.read(fd, buf, toxfer) == toxfer
                 && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
@@ -1087,13 +1441,13 @@
             size -= toxfer;
         }
     }
-        
+
     cenforce(core.sys.posix.unistd.close(fdw) != -1, from);
-        
+
     utimbuf utim = void;
     utim.actime = cast(time_t)statbuf.st_atime;
     utim.modtime = cast(time_t)statbuf.st_mtime;
-        
+
     cenforce(utime(toz, &utim) != -1, from);
 }
 
@@ -1146,7 +1500,7 @@
     assert(ftm1 + 1000 == ftm2);
 }
 +/
-    
+
 /****************************************************
 Remove directory and all of its content and subdirectories,
 recursively.
@@ -1154,13 +1508,19 @@
 
 void rmdirRecurse(in char[] pathname)
 {
-    // all children, recursively depth-first
-    foreach (DirEntry e; dirEntries(pathname.idup, SpanMode.depth))
+    if(pathname.issymlink())
+        remove(pathname);
+    else
     {
-        e.isdir ? rmdir(e.name) : remove(e.name);
+        // all children, recursively depth-first
+        foreach (DirEntry e; dirEntries(pathname.idup, SpanMode.shallow))
+        {
+            isdir(e.linkAttributes) ? rmdirRecurse(e.fullname) : remove(e.fullname);
+        }
+
+        // the dir itself
+        rmdir(pathname);
     }
-    // the dir itself
-    rmdir(pathname);
 }
 
 version(Windows) unittest
@@ -1173,11 +1533,23 @@
 
 version(Posix) unittest
 {
-    auto d = "/tmp/deleteme/a/b/c/d/e/f/g";
-    enforce(collectException(mkdir(d)));
-    mkdirRecurse(d);
-    rmdirRecurse("/tmp/deleteme");
-    enforce(!exists("/tmp/deleteme"));
+    {
+        auto d = "/tmp/deleteme/a/b/c/d/e/f/g";
+        enforce(collectException(mkdir(d)));
+        mkdirRecurse(d);
+        std.process.system("ln -sf /tmp/deleteme/a/b/c /tmp/deleteme/link");
+        rmdirRecurse("/tmp/deleteme/link");
+        enforce(exists(d));
+        rmdirRecurse("/tmp/deleteme");
+        enforce(!exists("/tmp/deleteme"));
+    }
+    {
+        auto d = "/tmp/deleteme/a/b/c/d/e/f/g";
+        mkdirRecurse(d);
+        std.process.system("ln -sf /tmp/deleteme/a/b/c /tmp/deleteme/link");
+        rmdirRecurse("/tmp/deleteme");
+        enforce(!exists("/tmp/deleteme"));
+    }
 }
 
 unittest
@@ -1203,9 +1575,9 @@
 
 unittest
 {
-    listdir (".", delegate bool (DirEntry * de)
+    listdir (".", delegate bool (DirEntry* de)
     {
-        auto s = std.string.format("%s : c %s, w %s, a %s", de.name,
+        auto s = std.string.format("%s : c %s, w %s, a %s", de.fullname,
                 toUTCString (de.creationTime),
                 toUTCString (de.lastWriteTime),
                 toUTCString (de.lastAccessTime));
@@ -1236,12 +1608,13 @@
     string pathname;
     SpanMode mode;
 
-    private int doIt(D)(D dg, DirEntry * de)
+    private int doIt(D)(D dg, DirEntry* de)
     {
         alias ParameterTypeTuple!(D) Parms;
         static if (is(Parms[0] : const(char)[]))
         {
-            return dg(de.name);
+            string fullname = de.fullname;
+            return dg(fullname);
         }
         else static if (is(Parms[0] : DirEntry))
         {
@@ -1258,7 +1631,7 @@
     {
         int result = 0;
         // worklist used only in breadth-first traversal
-        string[] worklist = [ pathname ]; 
+        string[] worklist = [ pathname ];
 
         bool callback(DirEntry* de)
         {
@@ -1271,14 +1644,14 @@
                 result = doIt(dg, de);
                 if (!result && de.isdir)
                 {
-                    worklist ~= de.name;
+                    worklist ~= de.fullname;
                 }
                 break;
             default:
                 assert(mode == SpanMode.depth);
                 if (de.isdir)
                 {
-                    listdir(de.name, &callback);
+                    listdir(de.fullname, &callback);
                 }
                 if (!result)
                 {
@@ -1363,6 +1736,17 @@
     }
 }
 
+/***************************************************
+ * Get $(D DirEntry) for a file (or directory).
+ */
+
+DirEntry direntry(in char[] name)
+{
+    DirEntry dirEntry;
+    dirEntry.init(name);
+    return dirEntry;
+}
+
 /**
 Reads an entire file into an array.
 
@@ -1428,13 +1812,13 @@
 string[] listdir(in char[] pathname)
 {
     Appender!(string[]) result;
-    
+
     bool listing(string filename)
     {
         result.put(filename);
         return true; // continue
     }
-    
+
     listdir(pathname, &listing);
     return result.data;
 }
@@ -1488,45 +1872,45 @@
 string[] listdir(in char[] pathname, in char[] pattern)
 {
     Appender!(string[]) result;
-    
+
     bool callback(DirEntry* de)
     {
         if (de.isdir)
-            listdir(de.name, &callback);
+            listdir(de.fullname, &callback);
         else
         {
-            if (std.path.fnmatch(de.name, pattern))
-                result.put(de.name);
+            if (std.path.fnmatch(de.fullname, pattern))
+                result.put(de.fullname);
         }
         return true; // continue
     }
-    
+
     listdir(pathname, &callback);
     return result.data;
 }
-    
+
 /** Ditto */
-    
+
 string[] listdir(in char[] pathname, RegExp r)
 {
     Appender!(string[]) result;
-    
+
     bool callback(DirEntry* de)
     {
         if (de.isdir)
-            listdir(de.name, &callback);
+            listdir(de.fullname, &callback);
         else
         {
-            if (r.test(de.name))
-                result.put(de.name);
+            if (r.test(de.fullname))
+                result.put(de.fullname);
         }
         return true; // continue
     }
-    
+
     listdir(pathname, &callback);
     return result.data;
 }
-    
+
 /******************************************************
  * For each file and directory name in pathname[],
  * pass it to the callback delegate.
@@ -1571,8 +1955,10 @@
 {
     bool listing(DirEntry* de)
     {
-        return callback(std.path.getBaseName(de.name));
+        return callback(std.path.basename(de.fullname));
     }
-    
+
     listdir(pathname, &listing);
 }
+
+
