Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 1692

Show
Ignore:
Timestamp:
06/24/10 01:14:06 (14 years ago)
Author:
rsinfu
Message:

- Fixed bugzilla 3937: os.path.dirname fails on absolute path.
- Fixed bugzilla 4260: windows & basename.
- Made dirname() tolerant of excess path separators (e.g. "/usr//src").
- Fixed dirname() to deal with Windows' "current directory of a drive" case (e.g. "C:.").
- Made basename() more generic.
- Added more unittests for dirname() and basename().
- Changed version(Win32) to version(Windows) as the module doesn't depend on pointer size.
- Added guard assertion for unknown platforms.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/docsrc/changelog.dd

    r1688 r1692  
    1313    $(LI $(BUGZILLA 978): std.utf's toUTF* functions accept some invalid and reject some valid UTF) 
    1414    $(LI $(BUGZILLA 996): Error in doc on implicit conversion between pointer and array) 
    1515    $(LI $(BUGZILLA 2275): std.utf.toUTF16z() should return const(wchar)*) 
    1616    $(LI $(BUGZILLA 2872): Length, opIndex for Map) 
    1717    $(LI $(BUGZILLA 3202): std.math.pow cause dead loop) 
    1818    $(LI $(BUGZILLA 3355): std.string.cmp works incorrectly for mixed-type and different-length strings) 
    1919    $(LI $(BUGZILLA 3386): to!bool(string) is not implemented) 
    2020    $(LI $(BUGZILLA 3436): std.functional.compose with only one function) 
    2121    $(LI $(BUGZILLA 3439): std.range.Sequence.opIndex not consistent after calling popFront().) 
    2222    $(LI $(BUGZILLA 3447): std.file uses unconventional file permissions) 
     23    $(LI $(BUGZILLA 3937): os.path.dirname fails on absolute path) 
    2324    $(LI $(BUGZILLA 3961): Error with to!(somestruct)) 
    2425    $(LI $(BUGZILLA 4109): (reopened) writeln doesn't work with empty static array) 
    2526    $(LI $(BUGZILLA 4171): std.random.uniform does not work for a range of characters) 
     27    $(LI $(BUGZILLA 4260): windows & basename) 
    2628    $(LI $(BUGZILLA 4327): std.container.Array.Range.~this() tries to call free(T[])) 
    2729    $(LI $(BUGZILLA 4362): std.range.repeat and cycle do not have a .save() method 
    2830    $(LI $(BUGZILLA 4363): std.algorithm.Until is not a forward range) 
    2931    ) 
    3032) 
    3133 
    3234<div id=version> 
    3335$(UL 
    3436    $(NEW 048) 
    3537    $(NEW 047) 
  • trunk/phobos/std/path.d

    r1591 r1692  
    2020 *          Copyright Digital Mars 2000 - 2009. 
    2121 * Distributed under the Boost Software License, Version 1.0. 
    2222 *    (See accompanying file LICENSE_1_0.txt or copy at 
    2323 *          http://www.boost.org/LICENSE_1_0.txt) 
    2424 */ 
    2525module std.path; 
    2626 
    2727//debug=path;       // uncomment to turn on debugging printf's 
    2828//private import std.stdio; 
    2929 
    30 import std.contracts, std.conv, std.file, std.process, std.string, std.traits; 
     30import std.array, std.conv, std.file, std.process, std.string, std.traits; 
    3131import core.stdc.errno, core.stdc.stdlib; 
    3232 
    3333version(Posix) 
    3434{ 
    3535    private import core.sys.posix.pwd; 
    3636    private import core.exception : onOutOfMemoryError; 
    3737} 
    3838 
    3939version(Windows) 
    4040{ 
     
    9595 * also terminates the search. 
    9696 * 
    9797 * Returns: If a dot was found, characters to its right are 
    9898 * returned. If a path separator was found, or fullname didn't 
    9999 * contain any dots or path separators, returns null. 
    100100 * 
    101101 * Throws: Nothing. 
    102102 * 
    103103 * Examples: 
    104104 * ----- 
    105  * version(Win32
     105 * version(Windows
    106106 * { 
    107107 *     getExt(r"d:\path\foo.bat") // "bat" 
    108108 *     getExt(r"d:\path.two\bar") // null 
    109109 * } 
    110110 * version(Posix) 
    111111 * { 
    112112 *     getExt(r"/home/user.name/bar.")  // "" 
    113113 *     getExt(r"d:\\path.two\\bar")     // "two\\bar" 
    114114 *     getExt(r"/home/user/.resource")  // "resource" 
    115115 * } 
    116116 * ----- 
    117117 */ 
    118118 
    119119string getExt(string fullname) 
    120120{ 
    121121    auto i = fullname.length; 
    122122    while (i > 0) 
    123123    { 
    124     if (fullname[i - 1] == '.') 
    125         return fullname[i .. fullname.length]; 
    126     i--; 
    127     version(Win32) 
    128     { 
    129         if (fullname[i] == ':' || fullname[i] == '\\') 
    130         break; 
    131     } 
    132     version(Posix) 
    133     { 
    134         if (fullname[i] == '/') 
    135         break; 
    136     } 
     124        if (fullname[i - 1] == '.') 
     125            return fullname[i .. fullname.length]; 
     126        i--; 
     127        version(Windows) 
     128        { 
     129            if (fullname[i] == ':' || fullname[i] == '\\') 
     130                break; 
     131        } 
     132        else version(Posix) 
     133        { 
     134            if (fullname[i] == '/') 
     135                break; 
     136        } 
     137        else 
     138        { 
     139            static assert(0); 
     140        } 
    137141    } 
    138142    return null; 
    139143} 
    140144 
    141145unittest 
    142146{ 
    143147    debug(path) printf("path.getExt.unittest\n"); 
    144148    string result; 
    145149 
    146     version (Win32
     150    version (Windows
    147151    result = getExt("d:\\path\\foo.bat"); 
    148152    version (Posix) 
    149153    result = getExt("/path/foo.bat"); 
    150154    auto i = cmp(result, "bat"); 
    151155    assert(i == 0); 
    152156 
    153     version (Win32
     157    version (Windows
    154158    result = getExt("d:\\path\\foo."); 
    155159    version (Posix) 
    156160    result = getExt("d/path/foo."); 
    157161    i = cmp(result, ""); 
    158162    assert(i == 0); 
    159163 
    160     version (Win32
     164    version (Windows
    161165    result = getExt("d:\\path\\foo"); 
    162166    version (Posix) 
    163167    result = getExt("d/path/foo"); 
    164168    i = cmp(result, ""); 
    165169    assert(i == 0); 
    166170 
    167     version (Win32
     171    version (Windows
    168172    result = getExt("d:\\path.bar\\foo"); 
    169173    version (Posix) 
    170174    result = getExt("/path.bar/foo"); 
    171175 
    172176    i = cmp(result, ""); 
    173177    assert(i == 0); 
    174178 
    175179    result = getExt("foo"); 
    176180    i = cmp(result, ""); 
    177181    assert(i == 0); 
     
    186190 * also terminates the search. 
    187191 * 
    188192 * Returns: If a dot was found, characters to its left are 
    189193 * returned. If a path separator was found, or fullname didn't 
    190194 * contain any dots or path separators, returns null. 
    191195 * 
    192196 * Throws: Nothing. 
    193197 * 
    194198 * Examples: 
    195199 * ----- 
    196  * version(Win32
     200 * version(Windows
    197201 * { 
    198202 *     getName(r"d:\path\foo.bat") => "d:\path\foo" 
    199203 *     getName(r"d:\path.two\bar") => null 
    200204 * } 
    201205 * version(Posix) 
    202206 * { 
    203207 *     getName("/home/user.name/bar.")  => "/home/user.name/bar" 
    204208 *     getName(r"d:\path.two\bar") => "d:\path" 
    205209 *     getName("/home/user/.resource") => "/home/user/" 
    206210 * } 
    207211 * ----- 
    208212 */ 
    209213 
    210214string getName(string fullname) 
    211215{ 
    212216    auto i = fullname.length; 
    213217    while (i > 0) 
    214218    { 
    215     if (fullname[i - 1] == '.') 
    216         return fullname[0 .. i - 1]; 
    217     i--; 
    218     version(Win32) 
    219     { 
    220         if (fullname[i] == ':' || fullname[i] == '\\') 
    221         break; 
    222     } 
    223     version(Posix) 
    224     { 
    225         if (fullname[i] == '/') 
    226         break; 
    227     } 
     219        if (fullname[i - 1] == '.') 
     220            return fullname[0 .. i - 1]; 
     221        i--; 
     222        version(Windows) 
     223        { 
     224            if (fullname[i] == ':' || fullname[i] == '\\') 
     225                break; 
     226        } 
     227        else version(Posix) 
     228        { 
     229            if (fullname[i] == '/') 
     230                break; 
     231        } 
     232        else 
     233        { 
     234            static assert(0); 
     235        } 
    228236    } 
    229237    return null; 
    230238} 
    231239 
    232240unittest 
    233241{ 
    234242    debug(path) printf("path.getName.unittest\n"); 
    235243    string result; 
    236244 
    237245    result = getName("foo.bar"); 
    238246    auto i = cmp(result, "foo"); 
    239247    assert(i == 0); 
    240248 
    241249    result = getName("d:\\path.two\\bar"); 
    242     version (Win32
     250    version (Windows
    243251    i = cmp(result, ""); 
    244252    version (Posix) 
    245253    i = cmp(result, "d:\\path"); 
    246254    assert(i == 0); 
    247255} 
    248256 
    249257/************************** 
    250258 * Extracts the base name of a path and optionally chops off a 
    251259 * specific suffix. 
    252260 * 
     
    258266 * $(D_PARAM fullname) otherwise. If the kept portion has suffix 
    259267 * $(D_PARAM extension), remove that suffix. Return the remaining string. 
    260268 * 
    261269 * Returns: The portion of $(D_PARAM fullname) left after the path 
    262270 * part and the extension part, if any, have been removed. 
    263271 * 
    264272 * Throws: Nothing. 
    265273 * 
    266274 * Examples: 
    267275 * ----- 
    268  * version(Win32
     276 * version(Windows
    269277 * { 
    270278 *     basename(r"d:\path\foo.bat") => "foo.bat" 
    271279 *     basename(r"d:\path\foo", ".bat") => "foo" 
    272280 * } 
    273281 * version(Posix) 
    274282 * { 
    275283 *     basename("/home/user.name/bar.")  => "bar." 
    276284 *     basename("/home/user.name/bar.", ".")  => "bar" 
    277285 * } 
    278286 * ----- 
    279287 */ 
    280288 
    281 S basename(S)(S fullname, string extension = null) 
     289String basename(String, ExtString = string)(String fullname, ExtString extension = null) 
     290    if (isSomeString!(String) && isSomeString!(ExtString)) 
    282291out (result) 
    283292{ 
    284293    assert(result.length <= fullname.length); 
    285294} 
    286295body 
    287296{ 
    288    auto i = fullname.length; 
    289    for (; i > 0; i--) 
    290    
    291        version(Win32
    292        { 
    293             if (fullname[i - 1] == ':' || fullname[i - 1] == '\\'
     297    auto i = fullname.length; 
     298    for (; i > 0; i--) 
     299   
     300        version(Windows
     301        { 
     302            if (fullname[i - 1] == ':' || fullname[i - 1] == '\\' || fullname[i - 1] == '/'
    294303                break; 
    295        } 
    296       version(Posix) 
    297        { 
     304        } 
     305        else version(Posix) 
     306        { 
    298307            if (fullname[i - 1] == '/') 
    299308                break; 
    300         } 
    301     } 
    302     return chomp(fullname[i .. fullname.length], 
    303             extension ? extension : ""); 
     309        } 
     310        else 
     311        { 
     312            static assert(0); 
     313        } 
     314    } 
     315    return chomp(fullname[i .. fullname.length], 
     316            extension.length ? extension : ""); 
    304317} 
    305318 
    306319/** Alias for $(D_PARAM basename), kept for backward 
    307320 * compatibility. New code should use $(D_PARAM basename). */ 
    308321alias basename getBaseName; 
    309322 
    310323unittest 
    311324{ 
    312325    debug(path) printf("path.basename.unittest\n"); 
    313326    string result; 
     
    324337    version (Posix) 
    325338    result = basename("a/b"); 
    326339    assert(result == "b"); 
    327340 
    328341    version (Windows) 
    329342    result = basename("a\\b.cde", ".cde"); 
    330343    version (Posix) 
    331344    result = basename("a/b.cde", ".cde"); 
    332345    assert(result == "b"); 
    333346 
     347    version (Windows) 
     348    { 
     349        assert(basename("abc/xyz") == "xyz"); 
     350        assert(basename("abc/") == ""); 
     351        assert(basename("C:/a/b") == "b"); 
     352        assert(basename(`C:\a/b`) == "b"); 
     353    } 
     354 
     355    assert(basename("~/dmd.conf"w, ".conf"d) == "dmd"); 
     356    assert(basename("~/dmd.conf"d, ".conf"d) == "dmd"); 
     357    assert(basename("dmd.conf"w.dup, ".conf"d.dup) == "dmd"); 
    334358} 
    335359 
    336360/************************** 
    337361 * Extracts the directory part of a path. 
    338362 * 
    339363 * This function will search $(D fullname) from the end until the 
    340364 * first path separator or first character of $(D fullname) is 
    341365 * reached. Under Windows, the drive letter separator ($(I colon)) 
    342366 * also terminates the search. 
    343367 * 
    344368 * Returns: If a path separator was found, all the characters to its 
    345  * left are returned. Otherwise, $(D ".") is returned. 
    346  * 
    347  * Under Windows, the found path separator will be included in the 
    348  * returned string if it is preceeded by a colon. 
     369 * left without any trailing path separators are returned. Otherwise, 
     370 * $(D ".") is returned. 
     371 * 
     372 * The found path separator will be included in the returned string 
     373 * if and only if it represents the root. 
    349374 * 
    350375 * Throws: Nothing. 
    351376 * 
    352377 * Examples: 
    353378 * ----- 
    354  * version(Win32
     379 * version(Windows
    355380 * { 
    356381 *     assert(dirname(r"d:\path\foo.bat") == r"d:\path"); 
    357  *     assert(dirname(dirname(r"d:\path\foo.bat")) == r"d:\"); 
     382 *     assert(dirname(r"d:\path") == r"d:\"); 
     383 *     assert(dirname("d:foo.bat") == "d:."); 
     384 *     assert(dirname("foo.bat") == "."); 
    358385 * } 
    359386 * version(Posix) 
    360387 * { 
    361388 *     assert(dirname("/home/user") == "/home"); 
    362  *     assert(dirname(dirname("/home/user")) == ""); 
    363  * } 
    364  * ----- 
    365  */ 
    366  
    367 Char[] dirname(Char)(Char[] fullname) 
    368 
    369     auto i = fullname.length; 
    370     for (; i > 0; i--) 
    371     { 
    372         version(Win32) 
    373         { 
    374             if (fullname[i - 1] == ':') 
     389 *     assert(dirname("/home") == "/"); 
     390 *     assert(dirname("user") == "."); 
     391 * } 
     392 * ----- 
     393 */ 
     394 
     395String dirname(String)(String fullname) 
     396    if (isSomeString!(String)) 
     397
     398    Unqual!String s = fullname; 
     399 
     400    version (Posix) 
     401    { 
     402        enum immutable(String) sep    = .sep, 
     403                               curdir = .curdir; 
     404        for (; !s.empty; s.popBack) 
     405        { 
     406            if (s.endsWith(sep)) 
    375407                break; 
    376             if (fullname[i - 1] == sep[0] || fullname[i - 1] == altsep[0]) 
     408        } 
     409        if (s.empty) 
     410            return to!(String)(curdir); 
     411 
     412        // remove excess non-root slashes: "/home//" --> "/home" 
     413        while (s.length > sep.length && s.endsWith(sep)) 
     414            s.popBack; 
     415        return s; 
     416    } 
     417    else version (Windows) 
     418    { 
     419        enum immutable(String) sep    = .sep, 
     420                               altsep = .altsep, 
     421                               curdir = .curdir, 
     422                               drvsep = ":"; 
     423        bool foundSep; 
     424        for (; !s.empty; s.popBack) 
     425        { 
     426            if (uint withWhat = s.endsWith(sep, altsep, drvsep)) 
    377427            { 
    378                 // Leave separator when it's the starting character 
    379         // (current root) or when it's preceded by ':' 
    380         // (absolute root) 
    381                 if (i != 1 && fullname[i - 2] != ':') 
    382                     i--; 
     428                foundSep = (withWhat != 3); 
    383429                break; 
    384430            } 
    385431        } 
    386         version(Posix) 
    387         { 
    388             if (fullname[i - 1] == sep[0]) 
    389             {   i--; 
     432        if (!foundSep) 
     433        { 
     434            if (s.empty) 
     435                return to!(String)(curdir); 
     436            else 
     437                return to!(String)(s ~ curdir); // cases like "C:." 
     438        } 
     439 
     440        // remove excess non-root separators: "C:\\" --> "C:\" 
     441        while (s.endsWith(sep) || s.endsWith(altsep)) 
     442        { 
     443            auto ss = s.save; 
     444            s.popBack; 
     445            if (s.empty || s.endsWith(drvsep)) 
     446            { 
     447                s = ss; // preserve path separator representing root 
    390448                break; 
    391449            } 
    392450        } 
    393     } 
    394     return i == 0 ? to!(Char[])(".") : fullname[0 .. i]; 
     451        return s; 
     452    } 
     453    else // unknown platform 
     454    { 
     455        static assert(0); 
     456    } 
    395457} 
    396458 
    397459unittest 
    398460{ 
    399461    assert(dirname("") == "."); 
    400462    assert(dirname("fileonly") == "."); 
     463 
    401464    version (Posix) 
    402465    { 
    403466        assert(dirname("/path/to/file") == "/path/to"); 
    404     } 
    405     else 
    406     { 
    407         version (Win32) 
    408         { 
    409             assert(dirname(r"\path\to\file") == r"\path\to"); 
    410             assert(dirname(r"\foo") == r"\"); 
    411             assert(dirname(r"c:\foo") == r"c:\"); 
    412         } 
     467        assert(dirname("/home") == "/"); 
     468 
     469        assert(dirname("/dev/zero"w) == "/dev"); 
     470        assert(dirname("/dev/null"d) == "/dev"); 
     471        assert(dirname(".login"w.dup) == "."); 
     472        assert(dirname(".login"d.dup) == "."); 
     473 
     474        // doc example 
     475        assert(dirname("/home/user") == "/home"); 
     476        assert(dirname("/home") == "/"); 
     477        assert(dirname("user") == "."); 
     478    } 
     479    version (Windows) 
     480    { 
     481        assert(dirname(r"\path\to\file") == r"\path\to"); 
     482        assert(dirname(r"\foo") == r"\"); 
     483        assert(dirname(r"c:\foo") == r"c:\"); 
     484 
     485        assert(dirname("\\Windows"w) == "\\"); 
     486        assert(dirname("\\Users"d) == "\\"); 
     487        assert(dirname("ntuser.dat"w.dup) == "."); 
     488        assert(dirname("ntuser.dat"d.dup) == "."); 
     489 
     490        // doc example 
     491        assert(dirname(r"d:\path\foo.bat") == r"d:\path"); 
     492        assert(dirname(r"d:\path") == "d:\\"); 
     493        assert(dirname("d:foo.bat") == "d:."); 
     494        assert(dirname("foo.bat") == "."); 
    413495    } 
    414496} 
    415497 
    416498/** Alias for $(D_PARAM dirname), kept for backward 
    417499 * compatibility. New code should use $(D_PARAM dirname). */ 
    418500alias dirname getDirName; 
    419501 
    420502unittest 
    421503{ 
    422504    string filename = "foo/bar"; 
    423505    auto d = getDirName(filename); 
    424506    assert(d == "foo"); 
    425507} 
     508 
     509unittest // dirname + basename 
     510{ 
     511    static immutable Common_dirbasename_testcases = 
     512    [ 
     513        [ "/usr/lib"  , "/usr"   , "lib"    ], 
     514        [ "/usr/"     , "/usr"   , ""       ], 
     515        [ "/usr"      , "/"      , "usr"    ], 
     516        [ "/"         , "/"      , ""       ], 
     517 
     518        [ "var/run"   , "var"    , "run"    ], 
     519        [ "var/"      , "var"    , ""       ], 
     520        [ "var"       , "."      , "var"    ], 
     521        [ "."         , "."      , "."      ], 
     522 
     523        [ "/usr///lib", "/usr"   , "lib"    ], 
     524        [ "///usr///" , "///usr" , ""       ], 
     525        [ "///usr"    , "/"      , "usr"    ], 
     526        [ "///"       , "/"      , ""       ], 
     527        [ "var///run" , "var"    , "run"    ], 
     528        [ "var///"    , "var"    , ""       ], 
     529 
     530        [ "a/b/c"     , "a/b"    , "c"      ], 
     531        [ "a///c"     , "a"      , "c"      ], 
     532        [ "/\u7A74"   , "/"      , "\u7A74" ], 
     533        [ "/\u7A74/." , "/\u7A74", "."      ] 
     534    ]; 
     535 
     536    static immutable Windows_dirbasename_testcases = 
     537        Common_dirbasename_testcases ~ 
     538    [ 
     539        [ "C:\\Users\\7mi", "C:\\Users", "7mi"   ], 
     540        [ "C:\\Users\\"   , "C:\\Users", ""      ], 
     541        [ "C:\\Users"     , "C:\\"     , "Users" ], 
     542        [ "C:\\"          , "C:\\"     , ""      ], 
     543 
     544        [ "C:Temp"        , "C:."      , "Temp"  ], 
     545        [ "C:"            , "C:."      , ""      ], 
     546        [ "\\dmd\\src"    , "\\dmd"    , "src"   ], 
     547        [ "\\dmd\\"       , "\\dmd"    , ""      ], 
     548        [ "\\dmd"         , "\\"       , "dmd"   ], 
     549 
     550        [ "C:/Users/7mi"  , "C:/Users" , "7mi"   ], 
     551        [ "C:/Users/"     , "C:/Users" , ""      ], 
     552        [ "C:/Users"      , "C:/"      , "Users" ], 
     553        [ "C:/"           , "C:/"      , ""      ], 
     554 
     555        [ "C:\\//WinNT"   , "C:\\"     , "WinNT" ], 
     556        [ "C://\\WinNT"   , "C:/"      , "WinNT" ], 
     557 
     558        [ `a\b\c`         , `a\b`      , "c"     ], 
     559        [ `a\\\c`         , "a"        , "c"     ] 
     560    ]; 
     561 
     562    version (Windows) 
     563        alias Windows_dirbasename_testcases testcases; 
     564    else 
     565        alias Common_dirbasename_testcases testcases; 
     566 
     567    foreach (tc; testcases) 
     568    { 
     569        string path = tc[0]; 
     570        string dir  = tc[1]; 
     571        string base = tc[2]; 
     572 
     573        assert(path.dirname == dir); 
     574        assert(path.basename == base); 
     575    } 
     576} 
     577 
    426578 
    427579/******************************** 
    428580 * Extracts the drive letter of a path. 
    429581 * 
    430582 * This function will search fullname for a colon from the beginning. 
    431583 * 
    432584 * Returns: If a colon is found, all the characters to its left 
    433585 * plus the colon are returned.  Otherwise, null is returned. 
    434586 * 
    435587 * Under Linux, this function always returns null immediately. 
     
    443595 */ 
    444596 
    445597String getDrive(String)(String fullname) if (isSomeString!(String)) 
    446598// @@@ BUG 2799 
    447599// out(result) 
    448600// { 
    449601//     assert(result.length <= fullname.length); 
    450602// } 
    451603body 
    452604{ 
    453    version(Win32
    454    
    455        foreach (i; 0 .. fullname.length) 
    456        { 
     605    version(Windows
     606   
     607        foreach (i; 0 .. fullname.length) 
     608        { 
    457609            if (fullname[i] == ':') 
    458610                return fullname[0 .. i + 1]; 
    459         } 
    460         return null; 
    461     } 
    462     version(Posix) 
    463     { 
    464         return null; 
    465     } 
     611        } 
     612        return null; 
     613    } 
     614    else version(Posix) 
     615    { 
     616        return null; 
     617    } 
     618    else 
     619    { 
     620        static assert(0); 
     621    } 
    466622} 
    467623 
    468624/**************************** 
    469625 * Appends a default extension to a filename. 
    470626 * 
    471627 * This function first searches filename for an extension and 
    472628 * appends ext if there is none. ext should not have any leading 
    473629 * dots, one will be inserted between filename and ext if filename 
    474630 * doesn't already end with one. 
    475631 * 
     
    551707 * Checks if path is absolute. 
    552708 * 
    553709 * Returns: non-zero if the path starts from the root directory (Linux) or 
    554710 * drive letter and root directory (Windows), 
    555711 * zero otherwise. 
    556712 * 
    557713 * Throws: Nothing. 
    558714 * 
    559715 * Examples: 
    560716 * ----- 
    561  * version(Win32
     717 * version(Windows
    562718 * { 
    563719 *     isabs(r"relative\path") => 0 
    564720 *     isabs(r"\relative\path") => 0 
    565721 *     isabs(r"d:\absolute") => 1 
    566722 * } 
    567723 * version(Posix) 
    568724 * { 
    569725 *     isabs("/home/user") => 1 
    570726 *     isabs("foo") => 0 
    571727 * } 
    572728 * ----- 
    573729 */ 
    574730 
    575731bool isabs(in char[] path) 
    576732{ 
    577733    auto d = getDrive(path); 
    578734    version (Windows) 
    579735    { 
    580     return d.length < path.length && 
    581         (path[d.length] == sep[0] || path[d.length] == altsep[0]); 
     736        return d.length < path.length && 
     737            (path[d.length] == sep[0] || path[d.length] == altsep[0]); 
     738    } 
     739    else version (Posix) 
     740    { 
     741        return d.length < path.length && path[d.length] == sep[0]; 
    582742    } 
    583743    else 
    584     return d.length < path.length && path[d.length] == sep[0]; 
     744    { 
     745        static assert(0); 
     746    } 
    585747} 
    586748 
    587749unittest 
    588750{ 
    589751    debug(path) printf("path.isabs.unittest\n"); 
    590752 
    591753    version (Windows) 
    592754    { 
    593755    assert(!isabs(r"relative\path")); 
    594756    assert(isabs(r"\relative\path")); 
     
    647809 * If p1 doesn't have a trailing path separator, one will be appended 
    648810 * to it before concatenating p2. 
    649811 * 
    650812 * Returns: p1 ~ p2. However, if p2 is an absolute path, only p2 
    651813 * will be returned. 
    652814 * 
    653815 * Throws: Nothing. 
    654816 * 
    655817 * Examples: 
    656818 * ----- 
    657  * version(Win32
     819 * version(Windows
    658820 * { 
    659821 *     join(r"c:\foo", "bar") => r"c:\foo\bar" 
    660822 *     join("foo", r"d:\bar") => r"d:\bar" 
    661823 * } 
    662824 * version(Posix) 
    663825 * { 
    664826 *     join("/foo/", "bar") => "/foo/bar" 
    665827 *     join("/foo", "/bar") => "/bar" 
    666828 * } 
    667829 * ----- 
     
    677839    // Focus on exactly two components 
    678840    version (Posix) 
    679841    { 
    680842        if (isabs(p2)) return p2.idup; 
    681843        if (p1.endsWith(sep[]) || altsep.length && p1.endsWith(altsep[])) 
    682844        { 
    683845            return cast(string) (p1 ~ p2); 
    684846        } 
    685847        return cast(string) (p1 ~ sep ~ p2); 
    686848    } 
    687     version (Windows) 
     849    else version (Windows) 
    688850    {  
    689851        if (!p2.length) 
    690852            return p1.idup; 
    691853        if (!p1.length) 
    692854            return p2.idup; 
    693855 
    694856        string p; 
    695857        const(char)[] d1; 
    696858 
    697859        if (getDrive(p2)) 
     
    718880            { 
    719881                p = cast(string) (p1 ~ p2); 
    720882            } 
    721883            else 
    722884            { 
    723885                p = cast(string)(p1 ~ sep ~ p2); 
    724886            } 
    725887        } 
    726888        return p; 
    727889    } 
     890    else // unknown platform 
     891    { 
     892        static assert(0); 
     893    } 
    728894} 
    729895 
    730896unittest 
    731897{ 
    732898    debug(path) printf("path.join.unittest\n"); 
    733899 
    734900    string p; 
    735901    int i; 
    736902 
    737903    p = join("foo", "bar"); 
    738     version (Win32
     904    version (Windows
    739905    i = cmp(p, "foo\\bar"); 
    740906    version (Posix) 
    741907    i = cmp(p, "foo/bar"); 
    742908    assert(i == 0); 
    743909 
    744     version (Win32
     910    version (Windows
    745911    {   p = join("foo\\", "bar"); 
    746912    i = cmp(p, "foo\\bar"); 
    747913    } 
    748914    version (Posix) 
    749915    {   p = join("foo/", "bar"); 
    750916    i = cmp(p, "foo/bar"); 
    751917    } 
    752918    assert(i == 0); 
    753919 
    754     version (Win32
     920    version (Windows
    755921    {   p = join("foo", "\\bar"); 
    756922    i = cmp(p, "\\bar"); 
    757923    } 
    758924    version (Posix) 
    759925    {   p = join("foo", "/bar"); 
    760926    i = cmp(p, "/bar"); 
    761927    } 
    762928    assert(i == 0); 
    763929 
    764     version (Win32
     930    version (Windows
    765931    {   p = join("foo\\", "\\bar"); 
    766932    i = cmp(p, "\\bar"); 
    767933    } 
    768934    version (Posix) 
    769935    {   p = join("foo/", "/bar"); 
    770936    i = cmp(p, "/bar"); 
    771937    } 
    772938    assert(i == 0); 
    773939 
    774     version(Win32
     940    version(Windows
    775941    { 
    776942        p = join("d:", "bar"); 
    777943        i = cmp(p, "d:bar"); 
    778944        assert(i == 0); 
    779945         
    780946        p = join("d:\\", "bar"); 
    781947        i = cmp(p, "d:\\bar"); 
    782948        assert(i == 0); 
    783949 
    784950        p = join("d:\\", "\\bar"); 
     
    815981 * 
    816982 * Under Windows, the comparison is done ignoring case. Under Linux 
    817983 * an exact match is performed. 
    818984 * 
    819985 * Returns: non zero if c1 matches c2, zero otherwise. 
    820986 * 
    821987 * Throws: Nothing. 
    822988 * 
    823989 * Examples: 
    824990 * ----- 
    825  * version(Win32
     991 * version(Windows
    826992 * { 
    827993 *     fncharmatch('a', 'b') => 0 
    828994 *     fncharmatch('A', 'a') => 1 
    829995 * } 
    830996 * version(Posix) 
    831997 * { 
    832998 *     fncharmatch('a', 'b') => 0 
    833999 *     fncharmatch('A', 'a') => 0 
    8341000 * } 
    8351001 * ----- 
    8361002 */ 
    8371003 
    8381004bool fncharmatch(dchar c1, dchar c2) 
    8391005{ 
    840     version (Win32) 
    841     { 
    842     if (c1 != c2) 
    843     { 
    844         if ('A' <= c1 && c1 <= 'Z') 
    845         c1 += cast(char)'a' - 'A'; 
    846         if ('A' <= c2 && c2 <= 'Z') 
    847         c2 += cast(char)'a' - 'A'; 
    848         return c1 == c2; 
    849     } 
    850     return true; 
    851     } 
    852     version (Posix) 
    853     { 
    854     return c1 == c2; 
     1006    version (Windows) 
     1007    { 
     1008        if (c1 != c2) 
     1009        { 
     1010            if ('A' <= c1 && c1 <= 'Z') 
     1011                c1 += cast(char)'a' - 'A'; 
     1012            if ('A' <= c2 && c2 <= 'Z') 
     1013                c2 += cast(char)'a' - 'A'; 
     1014            return c1 == c2; 
     1015        } 
     1016        return true; 
     1017    } 
     1018    else version (Posix) 
     1019    { 
     1020        return c1 == c2; 
     1021    } 
     1022    else 
     1023    { 
     1024        static assert(0); 
    8551025    } 
    8561026} 
    8571027 
    8581028/************************************ 
    8591029 * Matches a pattern against a filename. 
    8601030 * 
    8611031 * Some characters of pattern have special a meaning (they are 
    8621032 * <i>meta-characters</i>) and <b>can't</b> be escaped. These are: 
    8631033 * <p><table> 
    8641034 * <tr><td><b>*</b></td> 
     
    8781048 * further portions of the filename. 
    8791049 * 
    8801050 * Returns: non zero if pattern matches filename, zero otherwise. 
    8811051 * 
    8821052 * See_Also: fncharmatch(). 
    8831053 * 
    8841054 * Throws: Nothing. 
    8851055 * 
    8861056 * Examples: 
    8871057 * ----- 
    888  * version(Win32
     1058 * version(Windows
    8891059 * { 
    8901060 *     fnmatch("foo.bar", "*") => 1 
    8911061 *     fnmatch(r"foo/foo\bar", "f*b*r") => 1 
    8921062 *     fnmatch("foo.bar", "f?bar") => 0 
    8931063 *     fnmatch("Goo.bar", "[fg]???bar") => 1 
    8941064 *     fnmatch(r"d:\foo\bar", "d*foo?bar") => 1 
    8951065 * } 
    8961066 * version(Posix) 
    8971067 * { 
    8981068 *     fnmatch("Go*.bar", "[fg]???bar") => 0 
     
    9891159            break; 
    9901160        } 
    9911161    } 
    9921162    return ni >= filename.length; 
    9931163    } 
    9941164 
    9951165unittest 
    9961166{ 
    9971167    debug(path) printf("path.fnmatch.unittest\n"); 
    9981168 
    999     version (Win32
     1169    version (Windows
    10001170    assert(fnmatch("foo", "Foo")); 
    10011171    version (Posix) 
    10021172    assert(!fnmatch("foo", "Foo")); 
    10031173    assert(fnmatch("foo", "*")); 
    10041174    assert(fnmatch("foo.bar", "*")); 
    10051175    assert(fnmatch("foo.bar", "*.*")); 
    10061176    assert(fnmatch("foo.bar", "foo*")); 
    10071177    assert(fnmatch("foo.bar", "f*bar")); 
    10081178    assert(fnmatch("foo.bar", "f*b*r")); 
    10091179    assert(fnmatch("foo.bar", "f???bar"));