Wolfgang Thallergcc16 merge934c3885 months agoHistory
4319 lines · 135.4 KB · D
RawDownload
// Written in the D programming language. /** This module is used to manipulate path strings.     All functions, with the exception of $(LREF expandTilde) (and in some    cases $(LREF absolutePath) and $(LREF relativePath)), are pure    string manipulation functions; they don't depend on any state outside    the program, nor do they perform any actual file system actions.    This has the consequence that the module does not make any distinction    between a path that points to a directory and a path that points to a    file, and it does not know whether or not the object pointed to by the    path actually exists in the file system.    To differentiate between these cases, use $(REF isDir, std,file) and    $(REF exists, std,file).     Note that on Windows, both the backslash ($(D `\`)) and the slash ($(D `/`))    are in principle valid directory separators.  This module treats them    both on equal footing, but in cases where a $(I new) separator is    added, a backslash will be used.  Furthermore, the $(LREF buildNormalizedPath)    function will replace all slashes with backslashes on that platform.     In general, the functions in this module assume that the input paths    are well-formed.  (That is, they should not contain invalid characters,    they should follow the file system's path format, etc.)  The result    of calling a function on an ill-formed path is undefined.  When there    is a chance that a path or a file name is invalid (for instance, when it    has been input by the user), it may sometimes be desirable to use the    $(LREF isValidFilename) and $(LREF isValidPath) functions to check    this.     Most functions do not perform any memory allocations, and if a string is    returned, it is usually a slice of an input string.  If a function    allocates, this is explicitly mentioned in the documentation. $(SCRIPT inhibitQuickIndex = 1;)$(DIVC quickindex,$(BOOKTABLE,$(TR $(TH Category) $(TH Functions))$(TR $(TD Normalization) $(TD          $(LREF absolutePath)          $(LREF asAbsolutePath)          $(LREF asNormalizedPath)          $(LREF asRelativePath)          $(LREF buildNormalizedPath)          $(LREF buildPath)          $(LREF chainPath)          $(LREF expandTilde)))$(TR $(TD Partitioning) $(TD          $(LREF baseName)          $(LREF dirName)          $(LREF dirSeparator)          $(LREF driveName)          $(LREF pathSeparator)          $(LREF pathSplitter)          $(LREF relativePath)          $(LREF rootName)          $(LREF stripDrive)))$(TR $(TD Validation) $(TD          $(LREF isAbsolute)          $(LREF isDirSeparator)          $(LREF isRooted)          $(LREF isValidFilename)          $(LREF isValidPath)))$(TR $(TD Extension) $(TD          $(LREF defaultExtension)          $(LREF extension)          $(LREF setExtension)          $(LREF stripExtension)          $(LREF withDefaultExtension)          $(LREF withExtension)))$(TR $(TD Other) $(TD          $(LREF filenameCharCmp)          $(LREF filenameCmp)          $(LREF globMatch)          $(LREF CaseSensitive)))))     Authors:        Lars Tandle Kyllingstad,        $(HTTP digitalmars.com, Walter Bright),        Grzegorz Adam Hankiewicz,        Thomas K$(UUML)hne,        $(HTTP erdani.org, Andrei Alexandrescu)    Copyright:        Copyright (c) 2000-2014, the authors. All rights reserved.    License:        $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)    Source:        $(PHOBOSSRC std/path.d)*/module std.path;  import std.file : getcwd;static import std.meta;import std.range;import std.traits; version (OSX)    version = Darwin;else version (iOS)    version = Darwin;else version (TVOS)    version = Darwin;else version (WatchOS)    version = Darwin; version (StdUnittest){private:    struct TestAliasedString    {        string get() @safe @nogc pure nothrow return scope { return _s; }        alias get this;        @disable this(this);        string _s;    }     bool testAliasedString(alias func, Args...)(scope string s, scope Args args)    {        return func(TestAliasedString(s), args) == func(s, args);    }} /** String used to separate directory names in a path.  Under    POSIX this is a slash, under Windows a backslash.*/version (Posix)          enum string dirSeparator = "/";else version (Windows)   enum string dirSeparator = "\\";else static assert(0, "unsupported platform");    /** Path separator string.  A colon under POSIX, a semicolon    under Windows.*/version (Posix)          enum string pathSeparator = ":";else version (Windows)   enum string pathSeparator = ";";else static assert(0, "unsupported platform");    /** Determines whether the given character is a directory separator.     On Windows, this includes both $(D `\`) and $(D `/`).    On POSIX, it's just $(D `/`).*/bool isDirSeparator(dchar c)  @safe pure nothrow @nogc{    if (c == '/') return true;    version (Windows) if (c == '\\') return true;    return false;} ///@safe pure nothrow @nogc unittest{    version (Windows)    {        assert( '/'.isDirSeparator);        assert( '\\'.isDirSeparator);    }    else    {        assert( '/'.isDirSeparator);        assert(!'\\'.isDirSeparator);    }}  /*  Determines whether the given character is a drive separator.     On Windows, this is true if c is the ':' character that separates    the drive letter from the rest of the path.  On POSIX, this always    returns false.*/private bool isDriveSeparator(dchar c)  @safe pure nothrow @nogc{    version (Windows) return c == ':';    else return false;}  /*  Combines the isDirSeparator and isDriveSeparator tests. */version (Windows) private bool isSeparator(dchar c)  @safe pure nothrow @nogc{    return isDirSeparator(c) || isDriveSeparator(c);}version (Posix) private alias isSeparator = isDirSeparator;  /*  Helper function that determines the position of the last    drive/directory separator in a string.  Returns -1 if none    is found.*/private ptrdiff_t lastSeparator(R)(R path)if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||    isNarrowString!R){    auto i = (cast(ptrdiff_t) path.length) - 1;    while (i >= 0 && !isSeparator(path[i])) --i;    return i;}  version (Windows){    private bool isUNC(R)(R path)    if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||        isNarrowString!R)    {        return path.length >= 3 && isDirSeparator(path[0]) && isDirSeparator(path[1])            && !isDirSeparator(path[2]);    }     private ptrdiff_t uncRootLength(R)(R path)    if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||        isNarrowString!R)        in { assert(isUNC(path)); }        do    {        ptrdiff_t i = 3;        while (i < path.length && !isDirSeparator(path[i])) ++i;        if (i < path.length)        {            auto j = i;            do { ++j; } while (j < path.length && isDirSeparator(path[j]));            if (j < path.length)            {                do { ++j; } while (j < path.length && !isDirSeparator(path[j]));                i = j;            }        }        return i;    }     private bool hasDrive(R)(R path)    if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||        isNarrowString!R)    {        return path.length >= 2 && isDriveSeparator(path[1]);    }     private bool isDriveRoot(R)(R path)    if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||        isNarrowString!R)    {        return path.length >= 3 && isDriveSeparator(path[1])            && isDirSeparator(path[2]);    }}  /*  Helper functions that strip leading/trailing slashes and backslashes    from a path.*/private auto ltrimDirSeparators(R)(R path)if (isSomeFiniteCharInputRange!R || isNarrowString!R){    static if (isRandomAccessRange!R && hasSlicing!R || isNarrowString!R)    {        int i = 0;        while (i < path.length && isDirSeparator(path[i]))            ++i;        return path[i .. path.length];    }    else    {        while (!path.empty && isDirSeparator(path.front))            path.popFront();        return path;    }} @safe unittest{    import std.array;    import std.utf : byDchar;     assert(ltrimDirSeparators("//abc//").array == "abc//");    assert(ltrimDirSeparators("//abc//"d).array == "abc//"d);    assert(ltrimDirSeparators("//abc//".byDchar).array == "abc//"d);} private auto rtrimDirSeparators(R)(R path)if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||    isNarrowString!R){    static if (isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R)    {        auto i = (cast(ptrdiff_t) path.length) - 1;        while (i >= 0 && isDirSeparator(path[i]))            --i;        return path[0 .. i+1];    }    else    {        while (!path.empty && isDirSeparator(path.back))            path.popBack();        return path;    }} @safe unittest{    import std.array;     assert(rtrimDirSeparators("//abc//").array == "//abc");    assert(rtrimDirSeparators("//abc//"d).array == "//abc"d);     assert(rtrimDirSeparators(MockBiRange!char("//abc//")).array == "//abc");} private auto trimDirSeparators(R)(R path)if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||    isNarrowString!R){    return ltrimDirSeparators(rtrimDirSeparators(path));} @safe unittest{    import std.array;     assert(trimDirSeparators("//abc//").array == "abc");    assert(trimDirSeparators("//abc//"d).array == "abc"d);     assert(trimDirSeparators(MockBiRange!char("//abc//")).array == "abc");} /** This `enum` is used as a template argument to functions which    compare file names, and determines whether the comparison is    case sensitive or not.*/enum CaseSensitive : bool{    /// File names are case insensitive    no = false,     /// File names are case sensitive    yes = true,     /** The default (or most common) setting for the current platform.        That is, `no` on Windows and Mac OS X, and `yes` on all        POSIX systems except Darwin (Linux, *BSD, etc.).    */    osDefault = osDefaultCaseSensitivity} ///@safe unittest{    assert(baseName!(CaseSensitive.no)("dir/file.EXT", ".ext") == "file");    assert(baseName!(CaseSensitive.yes)("dir/file.EXT", ".ext") != "file");     version (Posix)        assert(relativePath!(CaseSensitive.no)("/FOO/bar", "/foo/baz") == "../bar");    else        assert(relativePath!(CaseSensitive.no)(`c:\FOO\bar`, `c:\foo\baz`) == `..\bar`);} version (Windows)     private enum osDefaultCaseSensitivity = false;else version (Darwin) private enum osDefaultCaseSensitivity = false;else version (Posix)  private enum osDefaultCaseSensitivity = true;else static assert(0); /**    Params:        cs = Whether or not suffix matching is case-sensitive.        path = A path name. It can be a string, or any random-access range of            characters.        suffix = An optional suffix to be removed from the file name.    Returns: The name of the file in the path name, without any leading        directory and with an optional suffix chopped off.     If `suffix` is specified, it will be compared to `path`    using `filenameCmp!cs`,    where `cs` is an optional template parameter determining whether    the comparison is case sensitive or not.  See the    $(LREF filenameCmp) documentation for details.     Note:    This function $(I only) strips away the specified suffix, which    doesn't necessarily have to represent an extension.    To remove the extension from a path, regardless of what the extension    is, use $(LREF stripExtension).    To obtain the filename without leading directories and without    an extension, combine the functions like this:    ---    assert(baseName(stripExtension("dir/file.ext")) == "file");    ---     Standards:    This function complies with    $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html,    the POSIX requirements for the 'basename' shell utility)    (with suitable adaptations for Windows paths).*/auto baseName(R)(return scope R path)if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R){    return _baseName(path);} /// dittoauto baseName(C)(return scope C[] path)if (isSomeChar!C){    return _baseName(path);} /// dittoinout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1)    (return scope inout(C)[] path, in C1[] suffix)    @safe pure //TODO: nothrow (because of filenameCmp())if (isSomeChar!C && isSomeChar!C1){    auto p = baseName(path);    if (p.length > suffix.length        && filenameCmp!cs(cast(const(C)[])p[$-suffix.length .. $], suffix) == 0)    {        return p[0 .. $-suffix.length];    }    else return p;} ///@safe unittest{    assert(baseName("dir/file.ext") == "file.ext");    assert(baseName("dir/file.ext", ".ext") == "file");    assert(baseName("dir/file.ext", ".xyz") == "file.ext");    assert(baseName("dir/filename", "name") == "file");    assert(baseName("dir/subdir/") == "subdir");     version (Windows)    {        assert(baseName(`d:file.ext`) == "file.ext");        assert(baseName(`d:\dir\file.ext`) == "file.ext");    }} @safe unittest{    assert(baseName("").empty);    assert(baseName("file.ext"w) == "file.ext");    assert(baseName("file.ext"d, ".ext") == "file");    assert(baseName("file", "file"w.dup) == "file");    assert(baseName("dir/file.ext"d.dup) == "file.ext");    assert(baseName("dir/file.ext", ".ext"d) == "file");    assert(baseName("dir/file"w, "file"d) == "file");    assert(baseName("dir///subdir////") == "subdir");    assert(baseName("dir/subdir.ext/", ".ext") == "subdir");    assert(baseName("dir/subdir/".dup, "subdir") == "subdir");    assert(baseName("/"w.dup) == "/");    assert(baseName("//"d.dup) == "/");    assert(baseName("///") == "/");     assert(baseName!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext");    assert(baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file");     {        auto r = MockRange!(immutable(char))(`dir/file.ext`);        auto s = r.baseName();        foreach (i, c; `file`)            assert(s[i] == c);    }     version (Windows)    {        assert(baseName(`dir\file.ext`) == `file.ext`);        assert(baseName(`dir\file.ext`, `.ext`) == `file`);        assert(baseName(`dir\file`, `file`) == `file`);        assert(baseName(`d:file.ext`) == `file.ext`);        assert(baseName(`d:file.ext`, `.ext`) == `file`);        assert(baseName(`d:file`, `file`) == `file`);        assert(baseName(`dir\\subdir\\\`) == `subdir`);        assert(baseName(`dir\subdir.ext\`, `.ext`) == `subdir`);        assert(baseName(`dir\subdir\`, `subdir`) == `subdir`);        assert(baseName(`\`) == `\`);        assert(baseName(`\\`) == `\`);        assert(baseName(`\\\`) == `\`);        assert(baseName(`d:\`) == `\`);        assert(baseName(`d:`).empty);        assert(baseName(`\\server\share\file`) == `file`);        assert(baseName(`\\server\share\`) == `\`);        assert(baseName(`\\server\share`) == `\`);         auto r = MockRange!(immutable(char))(`\\server\share`);        auto s = r.baseName();        foreach (i, c; `\`)            assert(s[i] == c);    }

Lines 1–500 of 4319.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t