www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Some path functions

reply Kramer <Kramer_member pathlink.com> writes:
I have some functions for the path module that I'd like to submit to the 
general public.  I haven't worked on them for about half a year and I 
had submitted them to Walter for inclusion into Phobos a while back, but 
he's got far more pressing issues (like bug fixes and spec. stability) 
to concern himself with.

The list of included functions:
getRoots
isRoot
containsRoot
normPath // normalize a path; similar to the Python version
          // http://docs.python.org/lib/module-os.path.html
isNormPath
normCase // normalize the case
normSep // normalize the separators for the given os
join // variadic join
absPath // includes a private isAbs because the one in std.path doesn't 
seem to be working
expandPath // similar to the Ruby version
            // http://www.rubycentral.com/ref/ref_c_file.html#expand_path

I offer the code free to anyone; use it as you may, it's public domain. 
  I hope it works, but offer no assurances on it's quality and am not 
responsible for it's use in other code.

It compiles clean under DMD 0.163 on Windows, but I haven't tested it on 
Linux since I wrote it -- I no longer have access to a Linux box.  The 
functions use an internal isAbs function because when I was writing the 
code I found the one in Phobos to not be accurate.  I'm probably wrong 
on that front, so to get the Phobos isAbs functionality, remove the 
internal one (the internal one returns a bool instead of an int, so any 
compares will need to be modified).

Hopefully someone will find it useful, but in the end, I just wanted to 
do my small share to contribute to this fantastic language.

Walter, keep up the good work on this language.  But I have to say, 
honestly, to the community, keep up the good work and motivation.  I've 
never come across a more intelligent and dedicated langauge community. 
And friendly by the way.  It's always a pleasure reading the newsgroups.

Onward, upward and D-ward!

-Kramer

P.S. - I'm having some trouble attaching the code so I'm just pasting it 
in this message.  Formatting will be screwy I'm sure, but I'm not sure 
how else to post.  I think my file is too large to send, so I'm leaving 
out the unittests in this message and will past them in another.




/**
  * Author: Joe Zuccarello
  * Date: February 2006
  *
  * By the author's permission, this code is considered in the public 
domain for modification and
  * redistribution at will.
  */

private import std.string,
                std.path,
                std.file,
                std.regexp,
                std.utf,
                std.stdarg,
                std.format;

version(Windows) private import std.c.windows.windows;

version(Windows)
{
extern(Windows)
{
     export
     {
         DWORD GetLogicalDriveStringsA(DWORD, LPTSTR);
         DWORD GetLogicalDriveStringsW(DWORD, LPWSTR);
     }
}
}

private static const char[] wSep = r"\",
                             lSep = "/",
                             rSeps = "[\\\\/]";  // For regexp use

void main() {}
/**
  * Returns all roots for the system.
  *
  * This will return all the roots that are know by the system.
  *
  * For Windows, it will be the drives that are available.
  * For Linux, it will always return root "/".
  *
  * Note, this function uses the Windows API to retrieve the roots.  If 
is ANSI characters are being
  * used, getRoots will attemp to convert it to UTF8.  If the UTF8 code 
throws a UTF exception,
  * that exception will not be caught and will be propogated to the 
caller code.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 14, 2006
  *
  * Returns: All roots for the system.
  *
  * Throws: (Windows) UtfException on conversion error from ANSI to 
UTF-8 if the OS is using ANSI.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     // Assume roots "A:\", "C:\" and "D:\" exist
  *     getRoots() => "A:\", "C:\", "D:\"
  * }
  * version(linux)
  * {
  *     getRoots() => "/"
  * }
  * ---------
  */
char[][] getRoots()
{
     char[][] rtnBuffer;

     version(Windows)
     {
     DWORD bufferLen = 50,
           rtnVal;
     char[] buffer;

     // Unicode
     void getDrivesW()
     {
         wchar[] wBuffer;
         wBuffer.length = bufferLen;
         rtnVal = GetLogicalDriveStringsW(bufferLen, wBuffer);
         buffer = std.utf.toUTF8(wBuffer);
     }

     // ANSI
     void getDrivesA()
     {
         char* tmpBuf = std.windows.charset.toMBSz(buffer);
         rtnVal = GetLogicalDriveStringsA(bufferLen, tmpBuf);
         buffer = std.windows.charset.fromMBSz(tmpBuf);
     }

     // Set initial buffer size
     buffer.length = bufferLen;

     if (useWfuncs)
     {
         getDrivesW();
     }
     else
     {
         getDrivesA();
     }

     while (rtnVal > bufferLen)
     {
         bufferLen = rtnVal;
         buffer.length = bufferLen;
         if (useWfuncs)
         {
             getDrivesW();
         }
         else
         {
             getDrivesA();
         }
     }
     rtnBuffer.length = buffer.length;

     int j = 0;
     for (int i, nullCnt = 0; i < buffer.length && nullCnt < 2; i++)
     {
         if (buffer[i..(i + 1)] != "\0")
         {
             rtnBuffer[j] ~= buffer[i..(i + 1)];
             nullCnt = 0;
         }
         else
         {
             nullCnt++;
             j++;
         }
     }
     rtnBuffer.length = j - 1;

     return rtnBuffer.dup;
     }
     else version(linux)
     {
     rtnBuffer ~= "/";

     return rtnBuffer.dup;
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

/**
  * Returns true/false whether a path is a root of the system.
  *
  * This can be used to test a path to see if it's a root of the system.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 14, 2006
  *
  * Returns: true/false whether a path is a root of the system.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     // Assume on Windows, c:\ exists
  *     isRoot(r"c:\") => true
  * }
  * version(linux)
  * {
  *     isRoot("/") => true
  * }
  * ---------
  */
bool isRoot(char[] path, bool caseSensitive = false)
{
     char[][] roots = getRoots();

     foreach (char[] x; roots)
     {
         if (caseSensitive == true)
         {
             if (x == path)
             {
                 return true;
             }
         }
         else
         {
             if (std.string.tolower(x) == std.string.tolower(path))
             {
                 return true;
             }
         }
     }

     return false;
}

/**
  * Returns true/false whether a path contains a root of the system.
  *
  * This can be used to test a path, to determine if it starts with a 
system root.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: true/false whether a path starts with a system root.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     // Assume on Windows, c:\ exists
  *     containsRoot(r"c:\directory\file") => true
  *     containsRoot(r"\directory\file") => false
  * }
  * version(linux)
  * {
  *     containsRoot("/usr/d/src") => true
  *     containsRoot("../d/src") => false
  *     containsRoot("\d/src") => false
  * }
  * ---------
  */
bool containsRoot(char[] path, bool caseSensitive = false)
{
     char[][] roots = getRoots();

     foreach (char[] x; roots)
     {
         if (caseSensitive == true)
         {
             if (std.string.find(path, x) == 0)
             {
                 return true;
             }
         }
         else
         {
             if (std.string.ifind(path, x) == 0)
             {
                 return true;
             }
         }
     }

     return false;
}

/**
  * Test whether a path is normalized.
  *
  * Use this to test whether a path is normalized.
  *
  * Note: This function does not handle UNC paths.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: true/false whether a path is normalized.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     isNormPath(r"directory1\..\directory2\file\.") => false
  *     // This one returns true, because there's no parent directory to 
collapse to.
  *     isNormPath(r"..\directory\file") => true
  * }
  * version(linux)
  * {
  *     isNormPath("/dir/../file") => false
  *     isNormPath("/file") => true
  * }
  * ---------
  */
bool isNormPath(char[] path)
{
     RegExp re;

     version(Windows)
     {
     // Special cases
     if (path == "." || path == ".." || (path == r"\" || path == "/") ||
         std.regexp.find(path, "^\\.\\." ~ "(" ~ rSeps ~ "\\.\\.)+") != 
-1 ||
         std.regexp.find(path, "^[a-zA-Z]*:" ~ rSeps ~ "$") != -1)
     {
         return true;
     }
     else
     {
         // Look for the following.  If found, then this is not a 
normalized path
         if (std.regexp.find(path, rSeps ~ "$") != -1 ||
             std.regexp.find(path, rSeps ~ "\\.\\." ~ "(" ~ rSeps ~ 
"|$)") != -1 ||
             std.regexp.find(path, rSeps ~ "\\." ~ "(" ~ rSeps ~ "|$)") 
!= -1 ||
             std.regexp.find(path, "^\\." ~ rSeps) != -1 || 
std.regexp.find(path, rSeps ~ "{2,}") != -1)
         {
             return false;
         }
         else
         {
             return true;
         }
     }
     }
     else version(linux)
     {
     // Special cases
     if (path == "." || path == ".." || (path == r"\" || path == "/") ||
         std.regexp.find(path, "^\\.\\." ~ "(" ~ rSeps ~ "\\.\\.)+") != -1)
     {
         return true;
     }
     else
     {
         // Look for the following.  If found, then this is not a 
normalized path
         if (std.regexp.find(path, lSep ~ "$") != -1 ||
             std.regexp.find(path, lSep ~ "\\.\\." ~ "(" ~ lSep ~ "|$)") 
!= -1 ||
             std.regexp.find(path, lSep ~ "\\." ~ "(" ~ lSep ~ "|$)") != 
-1 ||
             std.regexp.find(path, "^\\." ~ lSep) != -1 || 
std.regexp.find(path, lSep ~ "{2,}") != -1)
         {
             return false;
         }
         else
         {
             return true;
         }
     }
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

/**
  * Normalizes a path.
  *
  * This will normalize a path by collapsing redundant separators and 
parent/current directory
  * references.  It will also remove any trailing separators and 
normalize separators as appropriate
  * for the OS.
  *
  * Inspired by the Python v2.4.2 implementation.
  *
  * Note: This function does not handle UNC paths.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: A normalized path.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * normPath("/dir1/../dir2/./file/") => "/dir2/file"
  * normPath("/dir..../file/./") => "/dir..../file"
  * ---------
  */
char[] normPath(char[] path)
out(result)
{
     assert(isNormPath(result));
}
body
{
     int pcIdx, pcIdx2;
     char[][] pathComps;  // path components after splitting
     char[] result, drive;

     // Normalize the separators for the os
     path = normSep(path);

     // Sanity check.  No need to process a separator, curdir or pardir 
reference.
     if (path != sep && path != curdir && path != pardir)
     {
         // Remove the drive from the path
         version(Windows)
         {
         int idx = std.string.find(path, ":");
         drive ~= idx != -1 ? path[0..(idx + 1)] : "";
         if (idx != -1)
         {
             if ((idx + 1) < path.length)
             {
                 path = path[(idx + 1)..$];
             }
             else
             {
                 path = "";
             }
         }
         }

         // Remove repeating separators
         path = std.string.squeeze(path, sep);

         // If there's an initial separator even after a drive, save it off
         if (path != "")
         {
             if (path[0..1] == sep)
             {
                 drive ~= sep;
             }
         }

         // Split the path components
         pathComps = std.string.split(path, sep);

         while (pcIdx < pathComps.length)
         {
             // Current directory
             if (pathComps[pcIdx] == curdir)
             {
                 if (pathComps.length == 1)
                 {
                     pathComps.length = 0;
                 }
                 else if (pathComps.length > 1)
                 {
                     // At the beginning
                     if (pcIdx == 0)
                     {
                         pathComps = pathComps[1..$];
                     }
                     // At the end
                     else if ((pcIdx + 1) == pathComps.length)
                     {
                         pathComps = pathComps[0..pcIdx];
                     }
                     // In the middle
                     else
                     {
                         pathComps = pathComps[0..pcIdx] ~ 
pathComps[(pcIdx + 1)..$];
                     }
                 }
             }
             // Parent directory reference
             else if (pathComps[pcIdx] == pardir)
             {
                 if (pathComps.length == 1)
                 {
                     pcIdx++;
                 }
                 else if (pathComps.length > 1)
                 {
                     // At the beginning
                     if (pcIdx == 0)
                     {
                         // We don't know what to do with this, so move on
                         pcIdx++;
                     }
                     // Found a reference but there was a separator 
before it.  Need
                     // to remove this reference.
                     else if (pcIdx == 1 && pathComps[(pcIdx - 1)] == "")
                     {
                         // Delete the reference
                         if ((pcIdx + 1) < pathComps.length)
                         {
                             pathComps = pathComps[0..pcIdx] ~ 
pathComps[(pcIdx + 1)..$];
                             pcIdx--;
                         }
                         else
                         {
                             pathComps = pathComps[0..pcIdx];
                         }
                     }
                     else
                     {
                         if (pathComps[(pcIdx - 1)] != pardir)
                         {
                             if ((pcIdx + 1) < pathComps.length)
                             {
                                 // Delete the reference and the 
preceding entry
                                 pathComps = pathComps[0..(pcIdx - 1)] ~ 
pathComps[(pcIdx + 1)..$];
                                 pcIdx--;
                             }
                             // End of line
                             else
                             {
                                 pathComps = pathComps[0..(pcIdx - 1)];
                             }
                         }
                         else
                         {
                             pcIdx++;
                         }
                     }
                 }
             }
             // Something else
             else
             {
                 pcIdx++;
             }
         }

         // Delete any blank chunks out of the array for joining later
         for (int i = 0; i < pathComps.length; i++)
         {
             if (pathComps[i] == "")
             {
                 if (pathComps.length == 1)
                 {
                     pathComps.length = 0;
                 }
                 else if (pathComps.length > 1)
                 {
                     // At the beginning
                     if (i == 0)
                     {
                         pathComps = pathComps[1..$];
                     }
                     // At the end
                     else if ((i + 1) == pathComps.length)
                     {
                         pathComps = pathComps[0..i];
                     }
                     // In the middle.  This should already have been 
taken care of from the logic near
                     // the top of this function from using the squeeze 
and then split, there shouldn't be
                     // any blank chunks in the middle.
                 }
             }
         }

         result = std.string.join(pathComps, sep);
     }
     // Path was either a separator, curdir or pardir reference
     else
     {
         result = path;
     }


     if (result == "" && drive == "")
     {
         result = curdir;
     }
     else
     {
         result = drive ~ result;
     }

     return result.dup;
}

/**
  * Normalize the case and separators of a path.
  *
  * This will normalize the case for a path depending on the operating 
system in use.  For case
  * -insensitive os's (such as Windows), the path will be lower-cased. 
For case sensitive os's (such
  * as Linux), the path case will not be changed.  On Windows, forward 
slashes will be converted to
  * backward slashes and on Linux, backward slashes will be converted to 
forward slashes.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: Normalized case and separators for a path.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     normCase(r"C:\directory1\Subdirectory/FILE) => 
"c:\directory1\subdirectory\file"
  * }
  * version(linux)
  * {
  *     normCase(r"\usr/src\Path.d") => "\usr/src\Path.d"
  * }
  * ---------
  */
char[] normCase(char[] path)
{
     version(Windows)
     {
     // Take care of the case
     path = std.string.tolower(path);
     }

     path = normSep(path);

     return path.dup;
}

/**
  *
  * Normalizes the separators in a path.
  *
  * Use this to normalize separators as appropriate for the operating 
system in use.  On Windows,
  * forward slashes * will be converted to backward slashes.  On Linux, 
the path will just be
  * returned.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: Normalized separators for a path.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     normSep(r"c:/directory\file") => "c:\directory\file"
  * }
  * version(linux)
  * {
  *     normSep(r"/dir1\dir2\dir3/file") => "/dir1\dir2\dir3/file"
  * }
  * ---------
  */
char[] normSep(char[] path)
{
     version(Windows)
     {
     // Convert separators
     if (std.regexp.find(path, lSep) != -1)
     {
         path = std.string.replace(path, lSep, wSep);

         return path.dup;
     }
     else
     {
         return path;
     }
     }
     else version(linux)
     {
     return path;
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

/**
  * Joins an arbitrary number of paths together, using std.path.join as 
the main joining component.
  *
  * This will take an arbitrary number of paths and join them by passing 
them to std.path.join to do
  * the actual joining.  *** Join rules follow that of std.path.join ***
  *
  * Note, this function may attempt to convert non-UTF8 characters to 
UTF8.  If the UTF8 code throws
  * a UTF exception, that exception will not be caught and will be 
propogated to the caller code.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: A path joined of one or more separate paths.
  *
  * Throws: UtfException on error from conversion of wchar or dchar 
parameters.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * join(r"\dir1", "dir2", "dir3/file") => "\dir1\dir2\dir3/file"
  * ---------
  */
char[] join(...)
{
     char[] pathX, rtnPath;

     char[] toStringW(wchar c)
     {
         wchar[] result;
         result.length = 1;
         result[0] = c;
         return std.utf.toUTF8(result);
     }

     char[] toStringD(dchar c)
     {
         dchar[] result;
         result.length = 1;
         result[0] = c;
         return std.utf.toUTF8(result);
     }

     for (int i = 0; i < _arguments.length; i++)
     {
         if (_arguments[i] == typeid(char[]))
         {
             pathX = va_arg!(char[])(_argptr);
         }
         else if (_arguments[i] == typeid(wchar[]))
         {
             pathX = std.string.toUTF8(va_arg!(wchar[])(_argptr));
         }
         else if (_arguments[i] == typeid(dchar[]))
         {
             pathX = std.string.toUTF8(va_arg!(dchar[])(_argptr));
         }
         else if (_arguments[i] == typeid(char))
         {
             pathX = std.string.toString(va_arg!(char)(_argptr));
         }
         else if (_arguments[i] == typeid(wchar))
         {
             pathX = toStringW(va_arg!(wchar)(_argptr));
         }
         else if (_arguments[i] == typeid(dchar))
         {
             pathX = toStringD(va_arg!(dchar)(_argptr));
         }

         if (pathX != "")
         {
             rtnPath = std.path.join(rtnPath, pathX);
             pathX = "";
         }
     }

     return rtnPath.dup;
}

/**
  * Returns a normalized absolutized path.
  *
  * If the path is not absolute, it will be joined with the current 
working directory.  If it is an
  * absolute path, nothing will be joined with it.  In either case, the 
path will also be checked to
  * see if it is normalized.  If it's not, it will be normalized.
  *
  * Note: This function does not handle UNC paths.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: A normalized absolutized path.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     // Assume c:\ is the current working directory
  *     absPath(r"file") => "c:\file"
  *     absPath(r"c:\d/src\project") => "c:\d\src\project"
  *     absPath(r".\dir\file\..\dir2\file2") => "c:\dir\dir2\file2"
  * }
  * version(linux)
  * {
  *     // Assume /usr is the current working directory
  *     absPath("d/bin") => "/usr/d/bin"
  *     absPath("/d/lib") => "/d/lib"
  *     absPath("d/src/../file") => "/usr/d/file"
  * }
  * ---------
  */
char[] absPath(char[] path)
out(result)
{
     assert(isNormPath(result));
}
body
{
     bool changed;

     version(Windows)
     {
     // Path is not absolute
     //if (std.regexp.find(path, "^[a-zA-Z]*:\\\\") == -1)
     if (isAbs(path) == false)
     {
         path = std.path.join(getcwd(), path);
         changed = true;
     }
     }
     else version(linux)
     {
     // Path is not absolute
     //if (path[0..1] != r"\" && path[0..1] != "/")
     if (isAbs(path) == false)
     {
         path = std.path.join(getcwd(), path);
         changed = true;
     }
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }

     // Normalize the path
     if (isNormPath(path) == false)
     {
         path = normPath(path);
         changed = true;
     }

     if (changed == true)
     {
         return path.dup;
     }
     else
     {
         return path;
     }
}

private bool isAbs(char[] path)
{
     version(Windows)
     {
     if (std.regexp.find(path, "^[a-zA-Z]*:\\\\") != -1)
     {
         return true;
     }
     else
     {
         return false;
     }
     }
     else version(linux)
     {
     if (path[0..1] == "/")
     {
         return true;
     }
     else
     {
         return false;
     }
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

/**
  * Expands a path into a normalized absolutized path, with a optional 
reference directory to use
  * with relative paths.
  *
  * This will take a path and expand it into a normalized absolutized 
version of itself.  An optional
  * reference directory can be provided as well.  If the path passed in 
is relative, the reference
  * directory will be used to precede the path.  If the reference 
directory is a relative directory,
  * the reference directory and the path will be appended to the current 
working directory.
  *
  * Note: This function does not handle UNC paths.
  *
  * Authors: Joe Zuccarello
  *
  * Date: February 15, 2006
  *
  * Returns: A normalized absolutized path.
  *
  * Version:
  *
  * License: Public domain.
  *
  * Examples:
  * ---------
  * version(Windows)
  * {
  *     // Assume c:\ is the current working directory
  *     expandPath("file") => "c:\file"
  *     expandPath(r"\dir\file") => "c:\dir\file"
  *     expandPath("file", r"\dir") => "c:\dir\file"
  * }
  * version(linux)
  * {
  *     // Assume /usr is the current working directory
  *     expandPath("file") => "/usr/file"
  *     expandPath(r"/dir/file") => "/usr/dir/file"
  *     expandPath("file", "/dir") => "/usr/dir/file"
  * }
  * ---------
  */
char[] expandPath(char[] path, char[] dir = "")
{
     char[] result;

     if (path != "")
     {
         // Path is absolute; ditch the dir and return this after 
normalizing.
         if (isAbs(path) == true)
         {
             result = normPath(path);
         }
         // Path is not absolute
         else
         {
             if (dir != "")
             {
                 // Check if dir is absolute
                 if (isAbs(dir) == true)
                 {
                     result = normPath(dir ~ sep ~ path);
                 }
                 // Dir is not absolute, but it is a directory (at least 
that's
                 // what the caller is telling us.
                 else
                 {
                     result = normPath(getcwd() ~ sep ~ dir ~ sep ~ path);
                 }
             }
             // Dir is empty and path is not absolute
             else
             {
                 result = normPath(getcwd() ~ sep ~ path);
             }
         }
     }
     // Path is empty, check dir
     else
     {
         if (dir != "")
         {
             // Check if dir is absolute
             if (isAbs(dir) == true)
             {
                 result = normPath(dir);
             }
             else
             {
                 result = normPath(getcwd() ~ sep ~ dir);
             }
         }
         // Path and dir are empty
         else
         {
             result = "";
         }
     }

     return result.dup;
}
Jul 30 2006
next sibling parent Kramer <Kramer_member pathlink.com> writes:
Here's the unittests.  An excessive amount to be sure, but, hey, 
sometimes overly verifing your code can be fun. :)

// isRoot
unittest
{
     version(Windows)
     {
     // Valid roots
     assert(isRoot(getDrive(getcwd()) ~ sep));
     assert(isRoot(std.string.toupper(getDrive(getcwd()) ~ sep), true));

     // Invalid roots
     char[][] roots = getRoots();
     char[] lastRoot = roots[($ - 1)];
     char[] succRoot = std.string.succ(lastRoot[0..($ - 2)]) ~ 
lastRoot[($ - 2)..$];
     assert(!isRoot(succRoot));
     assert(!isRoot(std.string.tolower(getDrive(getcwd()) ~ sep), true));
     }
     else version(linux)
     {
     // Valid roots
     assert(isRoot("/"));
     assert(isRoot(getRoots()[0]));

     // Invalid roots
     assert(!isRoot("../"));
     assert(!isRoot(r"\"));
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}


// containsRoot
unittest
{
     version(Windows)
     {
     // Valid roots
     assert(containsRoot(getcwd()));
     assert(containsRoot(getDrive(getcwd()) ~ sep));
     assert(containsRoot(std.string.toupper(getcwd()), true));

     // Invalid roots
     int idx = std.string.find(getcwd(), getDrive(getcwd()));
     assert(!containsRoot(getcwd()[idx..(getDrive(getcwd()).length)]));
     assert(!containsRoot(std.string.tolower(getcwd()), true));
     assert(!containsRoot(std.string.tolower(getDrive(getcwd()) ~ sep), 
true));
     assert(!containsRoot(r"\directory\file"));
     }
     else version(linux)
     {
     // Valid roots
     assert(containsRoot("/usr/d/src"));

     // Invalid roots
     assert(!containsRoot("../d/src"));
     assert(!containsRoot(r"\d/src"));
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

// isNormPath
unittest
{
     version(Windows)
     {
     assert(!isNormPath(r" .\"));
     assert(!isNormPath(r".\"));
     assert(!isNormPath(r"\."));
     assert(!isNormPath(r"\.\"));
     assert(!isNormPath(r"..\"));
     assert(!isNormPath(r"\.."));
     assert(!isNormPath(r"\..\"));
     assert(!isNormPath(r"\\"));
     }
     else version(linux)
     {
     assert(isNormPath(r" .\"));
     assert(isNormPath(r".\"));
     assert(isNormPath(r"\."));
     assert(isNormPath(r"\.\"));
     assert(isNormPath(r"..\"));
     assert(isNormPath(r"\.."));
     assert(isNormPath(r"\..\"));
     assert(isNormPath(r"\\"));
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }

     // These patterns are not normalized paths
     assert(!isNormPath("//"));
     assert(!isNormPath(" ./"));
     assert(!isNormPath("./"));
     assert(!isNormPath("/."));
     assert(!isNormPath("/./"));
     assert(!isNormPath("../"));
     assert(!isNormPath("/.."));
     assert(!isNormPath("/../"));
     assert(!isNormPath(r"\/"));

     // These patterns are normalized paths
     assert(isNormPath(""));
     assert(isNormPath(" "));
     assert(isNormPath("  "));
     assert(isNormPath(" a b c"));
     assert(isNormPath("b"));
     assert(isNormPath("."));
     assert(isNormPath(".."));
     assert(isNormPath(r"\"));
     assert(isNormPath("/"));
     assert(isNormPath(r"..\.."));
     assert(isNormPath("../.."));

     // Now test the patterns that are part of the normPath unittest, 
duh duh dun...
     assert(isNormPath(normPath("")));
     assert(isNormPath(normPath(" ")));
     assert(isNormPath(normPath("  ")));
     assert(isNormPath(normPath(".")));
     assert(isNormPath(normPath("..")));
     assert(isNormPath(normPath(r"\")));
     assert(isNormPath(normPath("/")));
     assert(isNormPath(normPath("/.")));
     assert(isNormPath(normPath("./")));
     assert(isNormPath(normPath(r"\.")));
     assert(isNormPath(normPath(r".\")));
     assert(isNormPath(normPath(r".\dir1\dir2\file")));
     assert(isNormPath(normPath("./dir1/dir2/file")));
     assert(isNormPath(normPath(r"dir1\dir2\file\.")));
     assert(isNormPath(normPath("dir1/dir2/file/.")));
     assert(isNormPath(normPath(r"\dir1\dir2\file\.")));
     assert(isNormPath(normPath("/dir1/dir2/file/.")));
     assert(isNormPath(normPath(r".\dir1\dir2\file\.")));
     assert(isNormPath(normPath("./dir1/dir2/file/.")));
     assert(isNormPath(normPath(r".\.\dir1\dir2\file\.")));
     assert(isNormPath(normPath("././dir1/dir2/file/.")));
     assert(isNormPath(normPath(r".\.\dir1\dir2\file\.\.")));
     assert(isNormPath(normPath("././dir1/dir2/file/./.")));
     assert(isNormPath(normPath(r"dir1\.\dir2")));
     assert(isNormPath(normPath("dir1/./dir2")));
     assert(isNormPath(normPath(r"dir1\.\.\.\dir2\.\file")));
     assert(isNormPath(normPath("dir1/./././dir2/./file")));
     assert(isNormPath(normPath(r".\.")));
     assert(isNormPath(normPath("./.")));
     assert(isNormPath(normPath(r"\.\")));
     assert(isNormPath(normPath("/./")));
     assert(isNormPath(normPath(r"\.\.\.")));
     assert(isNormPath(normPath("/././.")));
     assert(isNormPath(normPath(r".\.\.\")));
     assert(isNormPath(normPath("./././")));
     assert(isNormPath(normPath("dir")));
     assert(isNormPath(normPath("c:")));
     assert(isNormPath(normPath(r"c:\..")));
     assert(isNormPath(normPath("c:/..")));
     assert(isNormPath(normPath(r"\dir")));
     assert(isNormPath(normPath("/dir")));
     assert(isNormPath(normPath(r"\dir1\file")));
     assert(isNormPath(normPath("/dir1/file")));
     assert(isNormPath(normPath(r"\dir1\file\")));
     assert(isNormPath(normPath("/dir1/file/")));
     assert(isNormPath(normPath(r"\dir1\file\..")));
     assert(isNormPath(normPath("/dir1/file/..")));
     assert(isNormPath(normPath(r"\dir1\dir2\..\file")));
     assert(isNormPath(normPath("/dir1/dir2/../file")));
     assert(isNormPath(normPath(r"\dir1\file..")));
     assert(isNormPath(normPath("/dir1/file..")));
     assert(isNormPath(normPath(r"\dir1\file\..\..")));
     assert(isNormPath(normPath("/dir1/file/../..")));
     assert(isNormPath(normPath("c:..file")));
     assert(isNormPath(normPath(r"c:\..\dir1\..\file")));
     assert(isNormPath(normPath("c:/../dir1/../file")));
     assert(isNormPath(normPath(r"c:..file\dir1")));
     assert(isNormPath(normPath("c:..file/dir1")));
     assert(isNormPath(normPath(r"c:..file\dir1\..")));
     assert(isNormPath(normPath("c:..file/dir1/..")));
     assert(isNormPath(normPath(".")));
     assert(isNormPath(normPath(r"c:\")));
     assert(isNormPath(normPath("c:/")));
     assert(isNormPath(normPath(r"c:\dir1\.\")));
     assert(isNormPath(normPath("c:/dir1/./")));
     assert(isNormPath(normPath(r"c:\dir1\.\file")));
     assert(isNormPath(normPath("c:/dir1/./file")));
     assert(isNormPath(normPath(r"c:\dir1\.\file\")));
     assert(isNormPath(normPath("c:/dir1/./file/")));
     assert(isNormPath(normPath(r"c:\dir1\..\file")));
     assert(isNormPath(normPath("c:/dir1/../file")));
     assert(isNormPath(normPath(r"c:\dir1\..\file\")));
     assert(isNormPath(normPath("c:/dir1/../file/")));
     assert(isNormPath(normPath(r"c:\dir1\..\file\.")));
     assert(isNormPath(normPath("c:/dir1/../file/.")));
     assert(isNormPath(normPath(r"c:\dir1\..\file\.\")));
     assert(isNormPath(normPath("c:/dir1/../file/./")));
     assert(isNormPath(normPath(r"c:\dir1\..\file\..")));
     assert(isNormPath(normPath("c:/dir1/../file/..")));
     assert(isNormPath(normPath(r"\\\dir1\file")));
     assert(isNormPath(normPath("///dir1/file")));
     assert(isNormPath(normPath(r"\\\dir1\..\dir2\file")));
     assert(isNormPath(normPath("///dir1/../dir2/file")));
     assert(isNormPath(normPath(r"\\\file")));
     assert(isNormPath(normPath("///file")));
     assert(isNormPath(normPath(r"..\..")));
     assert(isNormPath(normPath("../..")));
     assert(isNormPath(normPath(r"..\\\..")));
     assert(isNormPath(normPath("..///..")));
     assert(isNormPath(normPath(r"..\")));
     assert(isNormPath(normPath("../")));
     assert(isNormPath(normPath(r"..\\\\")));
     assert(isNormPath(normPath("..////")));
     assert(isNormPath(normPath(r".\")));
     assert(isNormPath(normPath("./")));
     assert(isNormPath(normPath(r"\.\file")));
     assert(isNormPath(normPath("/./file")));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep)));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep ~ 
"file")));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep ~ 
"file" ~ sep)));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"file")));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"file" ~ sep)));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"file" ~ sep ~ ".")));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"file" ~ sep ~ "." ~ sep)));
     assert(isNormPath(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"file" ~ sep ~ "..")));
     assert(isNormPath(normPath(sep ~ sep ~ sep ~ "dir1" ~ sep ~ "file")));
     assert(isNormPath(normPath(sep ~ sep ~ sep ~ "dir1" ~ sep ~ ".." ~ 
sep ~ "dir2" ~ sep ~ "file")));
     assert(isNormPath(normPath(sep ~ sep ~ sep ~ "file")));
     assert(isNormPath(normPath(".." ~ sep ~ "..")));
     assert(isNormPath(normPath(".." ~ sep ~ sep ~ sep ~ "..")));
     assert(isNormPath(normPath(".." ~ sep)));
     assert(isNormPath(normPath(".." ~ sep ~ sep ~ sep ~ sep)));
     assert(isNormPath(normPath("." ~ sep)));
     assert(isNormPath(normPath(sep ~ "." ~ sep ~ "file")));
     assert(isNormPath(normPath(r"~\dir")));
     assert(isNormPath(normPath("~/dir")));
     assert(isNormPath(normPath(sep ~ "..")));
     assert(isNormPath(normPath(sep ~ ".." ~ sep)));
     assert(isNormPath(normPath(sep ~ ".." ~ sep ~ "dir")));
     assert(isNormPath(normPath(sep ~ "..file")));
     assert(isNormPath(normPath(sep ~ ".file")));
     assert(isNormPath(normPath(".file")));
     assert(isNormPath(normPath("file.")));
     assert(isNormPath(normPath("file.file" ~ sep ~ "." ~ sep ~ "dir1" ~ 
sep ~ "dir2" ~ sep ~ "..")));
     assert(isNormPath(normPath("file$.")));
}

// normPath
unittest
{
     version(Windows)
     {
     assert(normPath("c:") == "c:");
     assert(normPath("c:" ~ sep ~ "..") == r"c:" ~ sep);
     assert(normPath("c:" ~ sep ~ ".." ~ sep ~ "dir1" ~ sep ~ ".." ~ sep 
~ "file") == "c:" ~ sep ~ "file");
     assert(normPath("c:" ~ sep) == "c:" ~ sep);
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep) == "c:" ~ 
sep ~ "dir1");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep ~ "file") == 
"c:" ~ sep ~ "dir1" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ "." ~ sep ~ "file" ~ 
sep) == "c:" ~ sep ~ "dir1" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ "file") == 
"c:" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ "file" ~ 
sep) == "c:" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ "file" ~ 
sep ~ ".") == "c:" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ "file" ~ 
sep ~ "." ~ sep) == "c:" ~ sep ~ "file");
     assert(normPath("c:" ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ "file" ~ 
sep ~ "..") == "c:" ~ sep);
     assert(normPath(r"\\/dir1////\\dir2//..\\\\file/./") == sep ~ 
"dir1" ~ sep ~ "file");
     }
     else version(linux)
     {
     assert(normPath("c:/../file") == "file");
     assert(normPath(r"c:\..\file") == r"c:\..\file");
     assert(normPath("~/dir") == "~/dir");
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }

     assert(normPath("") == ".");
     assert(normPath(r" ./") == r" .");
     assert(normPath(" ") == " ");
     assert(normPath("  ") == "  ");
     assert(normPath(".") == ".");
     assert(normPath("..") == "..");
     assert(normPath(sep) == sep);
     assert(normPath(sep ~ curdir) == sep);
     assert(normPath(curdir ~ sep) == curdir);
     assert(normPath(curdir ~ sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ 
"file") == "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(normPath("dir1" ~ sep ~ "dir2" ~ sep ~ "file" ~ sep ~ 
curdir) == "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(normPath(sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file" ~ sep ~ 
curdir) == sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(normPath(curdir ~ sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file" 
~ sep ~ curdir) == "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(normPath(curdir ~ sep ~ curdir ~ sep ~ "dir1" ~ sep ~ "dir2" 
~ sep ~ "file" ~ sep ~ curdir) == "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(normPath(curdir ~ sep ~ curdir ~ sep ~ "dir1" ~ sep ~ "dir2" 
~ sep ~ "file" ~ sep ~ curdir ~ sep ~ curdir) == "dir1" ~ sep ~ "dir2" ~ 
sep ~ "file");
     assert(normPath("dir1" ~ sep ~ curdir ~ sep ~ "dir2") == "dir1" ~ 
sep ~ "dir2");
     assert(normPath("dir1" ~ sep ~ curdir ~ sep ~ curdir ~ sep ~ curdir 
~ sep ~ "dir2" ~ sep ~ curdir ~ sep ~ "file") == "dir1" ~ sep ~ "dir2" ~ 
sep ~ "file");
     assert(normPath(curdir ~ sep ~ curdir) == curdir);
     assert(normPath(sep ~ curdir ~ sep) == sep);
     assert(normPath(sep ~ curdir ~ sep ~ curdir ~ sep ~ curdir) == sep);
     assert(normPath(curdir ~ sep ~ curdir ~ sep ~ curdir ~ sep) == curdir);
     assert(normPath("dir") == "dir");
     assert(normPath(sep ~ "dir") == sep ~ "dir");
     assert(normPath(sep ~ "dir1" ~ sep ~ "file") == sep ~ "dir1" ~ sep 
~ "file");
     assert(normPath(sep ~ "dir1" ~ sep ~ "file" ~ sep) == sep ~ "dir1" 
~ sep ~ "file");
     assert(normPath(sep ~ "dir1" ~ sep ~ "file" ~ sep ~ "..") == sep ~ 
"dir1");
     assert(normPath(sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ ".." ~ sep ~ 
"file") == sep ~ "dir1" ~ sep ~ "file");
     assert(normPath(sep ~ "dir1" ~ sep ~ "file..") == sep ~ "dir1" ~ 
sep ~ "file..");
     assert(normPath(sep ~ "dir1" ~ sep ~ "file" ~ sep ~ ".." ~ sep ~ 
"..") == sep);
     assert(normPath("c:..file") == "c:..file");
     assert(normPath("c:..file" ~ sep ~ "dir1") == "c:..file" ~ sep ~ 
"dir1");
     assert(normPath("c:..file" ~ sep ~ "dir1" ~ sep ~ "..") == "c:..file");
     assert(normPath(sep ~ sep ~ sep ~ "dir1" ~ sep ~ "file") == sep ~ 
"dir1" ~ sep ~ "file");
     assert(normPath(sep ~ sep ~ sep ~ "dir1" ~ sep ~ ".." ~ sep ~ 
"dir2" ~ sep ~ "file") == sep ~ "dir2" ~ sep ~ "file");
     assert(normPath(sep ~ sep ~ sep ~ "file") == sep ~ "file");
     assert(normPath(".." ~ sep ~ "..") == ".." ~ sep ~ "..");
     assert(normPath(".." ~ sep ~ sep ~ sep ~ "..") == ".." ~ sep ~ "..");
     assert(normPath(".." ~ sep) == "..");
     assert(normPath(".." ~ sep ~ sep ~ sep ~ sep) == "..");
     assert(normPath("." ~ sep) == ".");
     assert(normPath(sep ~ "." ~ sep ~ "file") == sep ~ "file");
     assert(normPath(r"~\dir") == r"~\dir");
     assert(normPath("~") == "~");
     assert(normPath(sep ~ "..") == sep);
     assert(normPath(sep ~ ".." ~ sep) == sep);
     assert(normPath(sep ~ ".." ~ sep ~ "dir") == sep ~ "dir");
     assert(normPath(sep ~ "..file") == sep ~ "..file");
     assert(normPath(sep ~ ".file") == sep ~ ".file");
     assert(normPath(".file") == ".file");
     assert(normPath("file.") == "file.");
     assert(normPath("file.file" ~ sep ~ "." ~ sep ~ "dir1" ~ sep ~ 
"dir2" ~ sep ~ "..") == "file.file" ~ sep ~ "dir1");
     assert(normPath("file$.") == "file$.");
}

// normCase
unittest
{
     version(Windows)
     {
     assert(normCase(r"C:/dir1\file1/dir2/File2") == 
r"c:\dir1\file1\dir2\file2");
     }
     else version(linux)
     {
     assert(normCase(r"/Dir1\file1\DIR2/file2") == 
r"/Dir1\file1\DIR2/file2");
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

// normSep
unittest
{
     version(Windows)
     {
     assert(normSep(r"c:/dir\file") == r"c:\dir\file");
     }
     else version(linux)
     {
     assert(normSep(r"\dir1/dir2\file") == r"\dir1/dir2\file");
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }
}

// join
unittest
{
     assert(join(r"\dir/file", "file2", r"file3\file4") == r"\dir/file" 
~ sep ~ "file2" ~ sep ~ r"file3\file4");
     assert(join(r"\dir1\file") == r"\dir1\file");
     assert(join(r"\dir1\file", r"dir2\file") == r"\dir1\file" ~ sep ~ 
r"dir2\file");
     assert(join(r"\dir1\file", r"dir2\file", r"dir3\file") == 
r"\dir1\file" ~ sep ~ r"dir2\file" ~ sep ~ r"dir3\file");
     assert(join("file1", "file2", "file3", "file4") == "file1" ~ sep ~ 
"file2" ~ sep ~ "file3" ~ sep ~ "file4");
     assert(join("file1", "file2"w, "file3"d, "file4"c) == "file1" ~ sep 
~ "file2" ~ sep ~ "file3" ~ sep ~ "file4");
     assert(join("f", "i"w, "l"d, "e"c) == "f" ~ sep ~ "i" ~ sep ~ "l" ~ 
sep ~ "e");
     char c = 'i'; wchar w = 'l'; dchar d = 'e';
     assert(join("f", c, w, d) == "f" ~ sep ~ "i" ~ sep ~ "l" ~ sep ~ "e");
     assert(join('f', 'i', 'l', 'e') == "f" ~ sep ~ "i" ~ sep ~ "l" ~ 
sep ~ "e");

     version(Windows)
     {
     assert(join("file1", r"\dir1\file2") == r"\dir1\file2");
     assert(join(r"\file1", r"\dir1") == r"\dir1");
     assert(join(r"\file1", "dir1", r"c:\dir2\file2") == r"c:\dir2\file2");
     assert(join("file1", r"\dir1", r"c:\dir2", r"d:\dir3", r"file3") == 
r"d:\dir3\file3");
     }
     version(linux)
     {
     assert(join("file1", r"/dir1\file2") == r"/dir1\file2");
     assert(join(r"\file1", r"\dir1") == r"\file1/\dir1");
     assert(join(r"\file1", "dir1", r"c:\dir2\file2") == 
r"\file1/dir1/c:\dir2\file2");
     assert(join("file1", r"\dir1", r"c:\dir2", r"d:\dir3", r"file3") == 
r"file1/\dir1/c:\dir2/d:\dir3/file3");
     }
}

// absPath
unittest
{
     version(Windows)
     {
     assert(absPath(r"c:\dir1\file1\") == r"c:\dir1\file1");
     }
     else version(linux)
     {
     assert(absPath("file") == std.path.join(getcwd(), "file"));
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }

     assert(absPath("file") == std.path.join(getcwd(), "file"));
     assert(absPath(r"\dir1\file") == std.path.join(getcwd(), 
r"\dir1\file"));
     assert(absPath(getcwd()));
     assert(absPath(absPath(getcwd())));
     assert(absPath(absPath(getDirName(getcwd()))));
}

// expandPath
unittest
{
     version(Windows)
     {
     assert(expandPath("", "u:") == getcwd() ~ sep ~ "u:");
     assert(expandPath("", r"u:\") == r"u:\");
     assert(expandPath("", r"u:\dir1") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\file\..") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\..\file\.") == r"u:\file");
     assert(expandPath("", r"\dir1\\:file") == getcwd() ~ r"\dir1\:file");
     assert(expandPath("", r"\:dir1\\file") == getcwd() ~ r"\:dir1\file");
     assert(expandPath("", "u:") == getcwd() ~ r"\u:");
     assert(expandPath("", r"u:\") == r"u:\");
     assert(expandPath("", r"u:\dir1") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\file\..") == r"u:\dir1");
     assert(expandPath("", r"u:\dir1\..\file\.") == r"u:\file");
     assert(expandPath("file", r"p:\" ~ "dir1") == r"p:\" ~ "dir1" ~ sep 
~ "file");
     assert(expandPath("file", r"p:\" ~ "dir1" ~ sep) == r"p:\" ~ "dir1" 
~ sep ~ "file");
     assert(expandPath("file", r"p:\" ~ "dir1" ~ sep ~ sep) == r"p:\" ~ 
"dir1" ~ sep ~ "file");
     assert(expandPath("file", "p:\\dir1\\\\dir2\\") == "p:" ~ sep ~ 
"dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file", 
"p:\\dir1\\\\dir2\\\\\\dir3\\\\\\\\dir4\\\\") == "p:" ~ sep ~ "dir1" ~ 
sep ~ "dir2" ~ sep ~ "dir3" ~ sep ~ "dir4" ~ sep ~ "file");
     assert(expandPath("file", "p:") == getcwd() ~ sep ~ "p:" ~ sep ~ 
"file");
     assert(expandPath("file", "p:\\") == "p:" ~ sep ~ "file");
     assert(expandPath("file" , "p:\\dir\\.") == "p:" ~ sep ~ "dir" ~ 
sep ~ "file");
     assert(expandPath("file" , "p:\\dir1\\.\\dir2\\") == "p:" ~ sep ~ 
"dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file" , "p:\\dir1\\.\\dir2\\.") == "p:" ~ sep ~ 
"dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file" , "p:\\dir1\\.\\dir2\\.\\") == "p:" ~ sep 
~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("", r"\") == getcwd());
     assert(expandPath("", r"\dir1") == getcwd() ~ r"\dir1");
     assert(expandPath("", r"\dir1\\file") == getcwd() ~ r"\dir1\file");
     assert(expandPath("", r"\..\dir1\\file") == getDirName(getcwd()) ~ 
r"\dir1\file");
     assert(expandPath("", r"\\\dir1\\file\\..\\dir2\\dir3\\..") == 
getcwd() ~ r"\dir1\dir2");
     assert(expandPath("file") == getcwd() ~ r"\file");
     assert(expandPath(r"\dir\file") == getcwd() ~ r"\dir\file");
     assert(expandPath("file", r"\dir") == getcwd() ~ r"\dir\file");
     assert(expandPath("file", r"....\") == getcwd() ~ sep ~ "...." ~ 
sep ~ "file");
     assert(expandPath("file", "a.\\") == getcwd() ~ sep ~ "a." ~ sep ~ 
"file");
     assert(expandPath("file", "..\\..") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", "..\\..\\") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", "..\\..\\..\\") == 
getDirName(getDirName(getDirName(getcwd()))) ~ sep ~ "file");
     assert(expandPath("file", "..\\..\\..\\..\\") == 
getDirName(getDirName(getDirName(getDirName(getcwd())))) ~ sep ~ "file");
     assert(expandPath("file" , ".\\dir") == getcwd() ~ sep ~ "dir" ~ 
sep ~ "file");
     assert(expandPath("file" , "dir1\\.") == getcwd() ~ sep ~ "dir1" ~ 
sep ~ "file");
     assert(expandPath("file" , "dir1\\.\\dir2") == getcwd() ~ sep ~ 
"dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file" , ".\\dir1\\.\\dir2") == getcwd() ~ sep ~ 
"dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file" , ".\\dir1\\.\\dir2\\.") == getcwd() ~ sep 
~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file" , ".\\..\\..\\.\\.") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", ".\\..\\.\\.\\..\\") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file" , r"\\.\\dir1\\.\\dir2\\.") == getcwd() ~ 
sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file", "\\.\\") == getcwd() ~ sep ~ "file");
     assert(expandPath("file", "\\") == getcwd() ~ sep ~ "file");
     assert(expandPath("file", "\\\\") == getcwd() ~ sep ~ "file");
     assert(expandPath("file", "\\..\\..") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", r"c:\..\..") == r"c:\file");
     assert(expandPath("file", "\\..\\..\\") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath(r"\dir1\dir2/file", "/refdir") == getcwd() ~ sep 
~ "refdir" ~ sep ~ "dir1" ~ sep ~ "dir2" ~ sep ~ "file");
     }
     else version(linux)
     {
     assert(expandPath("file") == getcwd() ~ "/file");
     assert(expandPath(r"/dir/file") == "/dir/file");
     assert(expandPath("file", "/dir") == "/dir/file");
     assert(expandPath("", r"\") == getcwd() ~ r"/\");
     assert(expandPath("", "/") == "/");
     assert(expandPath("", r"\dir1") == getcwd() ~ r"/\dir1");
     assert(expandPath("", r"\dir1\\file") == getcwd() ~ r"/\dir1\\file");
     assert(expandPath("", r"/dir1\\file///file2") == r"/dir1\\file/file2");
     assert(expandPath("", r"\..\dir1\\file") == getcwd() ~ 
r"/\..\dir1\\file");
     assert(expandPath("", r"\\\dir1\\file\\..\\dir2\\dir3\\..") == 
getcwd() ~ r"/\\\dir1\\file\\..\\dir2\\dir3\\..");
     assert(expandPath("", r"/dir1/file/../dir2/dir3/..") == "/dir1/dir2");
     assert(expandPath("file") == getcwd() ~ "/file");
     assert(expandPath(r"\dir\file") == getcwd() ~ r"/\dir\file");
     assert(expandPath(r"/dir\file") == r"/dir\file");
     assert(expandPath("file", r"\dir") == getcwd() ~ r"/\dir/file");
     assert(expandPath("file", r"....\") == getcwd() ~ sep ~ r"....\" ~ 
sep ~ "file");
     assert(expandPath("file", r"a.\") == getcwd() ~ sep ~ r"a.\" ~ sep 
~ "file");
     assert(expandPath("file", r"..\..") == getcwd() ~ r"/..\../file");
     assert(expandPath("file", "../..") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", r"..\..\") == getcwd() ~ r"/..\..\/file");
     assert(expandPath("file", "../../") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", r"..\..\..\") == getcwd() ~ 
r"/..\..\..\/file");
     assert(expandPath("file", "../../../") == 
getDirName(getDirName(getDirName(getcwd()))) ~ sep ~ "file");
     assert(expandPath("file", r"..\..\..\..\") == getcwd() ~ 
r"/..\..\..\..\/file");
     assert(expandPath("file", "../../../../") == 
getDirName(getDirName(getDirName(getDirName(getcwd())))) ~ sep ~ "file");
     assert(expandPath("file", r".\dir") == getcwd() ~ r"/.\dir/file");
     assert(expandPath("file", r"dir1\.") == getcwd() ~ r"/dir1\./file");
     assert(expandPath("file", r"dir1\.\dir2") == getcwd() ~ 
r"/dir1\.\dir2/file");
     assert(expandPath("file", r".\dir1\.\dir2") == getcwd() ~ 
r"/.\dir1\.\dir2/file");
     assert(expandPath("file", r".\dir1\.\dir2\.") == getcwd() ~ 
r"/.\dir1\.\dir2\./file");
     assert(expandPath("file", r".\..\..\.\.") == getcwd() ~ 
r"/.\..\..\.\./file");
     assert(expandPath("file", "./../.././.") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file", r".\..\.\.\..\") == getcwd() ~ 
r"/.\..\.\.\..\/file");
     assert(expandPath("file", "./../././../") == 
getDirName(getDirName(getcwd())) ~ sep ~ "file");
     assert(expandPath("file" , r"\\.\\dir1\\.\\dir2\\.") == getcwd() ~ 
r"/\\.\\dir1\\.\\dir2\\./file");
     assert(expandPath("file" , "//.//dir1//.//dir2//.") == sep ~ "dir1" 
~ sep ~ "dir2" ~ sep ~ "file");
     assert(expandPath("file", r"\.\") == getcwd() ~ r"/\.\/file");
     assert(expandPath("file", r"\") == getcwd() ~ r"/\/file");
     assert(expandPath("file", r"\\") == getcwd() ~ r"/\\/file");
     assert(expandPath("file", r"\..\..") == getcwd() ~ r"/\..\../file");
     assert(expandPath("file", "/../..") == "/file");
     assert(expandPath("file", r"\..\..\") == getcwd() ~ r"/\..\..\/file");
     assert(expandPath(r"\dir1\dir2/file", "/refdir") == 
r"/refdir/\dir1\dir2/file");
     }
     else
     {
     pragma(msg, "Unsupported OS");
     static assert(0);
     }

     assert(expandPath("") == "");
     assert(expandPath("file") == getcwd() ~ sep ~ "file");
     assert(expandPath("file", "") == getcwd() ~ sep ~ "file");
     assert(expandPath("file", "..") == getDirName(getcwd()) ~ sep ~ 
"file");
     assert(expandPath("..") == getDirName(getcwd()));
     assert(expandPath(getcwd()));
     assert(expandPath(getcwd(), ""));
     assert(expandPath(getcwd(), getcwd()));
     assert(expandPath(getcwd(), "refdir"));
}
Jul 30 2006
prev sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Kramer wrote:
 I have some functions for the path module that I'd like to submit to the 
 general public.  I haven't worked on them for about half a year and I 
 had submitted them to Walter for inclusion into Phobos a while back, but 
 he's got far more pressing issues (like bug fixes and spec. stability) 
 to concern himself with.
 
Seems nice (haven't used yet though, maybe in the future, for shell scripting). And indeed it seems like it would be a good addition for Phobos (std.path). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 17 2006
parent Kramer <Kramer_member pathlink.com> writes:
Bruno Medeiros wrote:
 Kramer wrote:
 I have some functions for the path module that I'd like to submit to 
 the general public.  I haven't worked on them for about half a year 
 and I had submitted them to Walter for inclusion into Phobos a while 
 back, but he's got far more pressing issues (like bug fixes and spec. 
 stability) to concern himself with.
Seems nice (haven't used yet though, maybe in the future, for shell scripting). And indeed it seems like it would be a good addition for Phobos (std.path).
Thanks. Hopefully, they're useful. :) -Kramer
Aug 17 2006