diff --git a/changelog.dd b/changelog.dd index 1a95f395ca0..10eed4b3b8d 100644 --- a/changelog.dd +++ b/changelog.dd @@ -23,6 +23,8 @@ $(BUGSTITLE Library Changes, of the type being iterated) $(LI $(XREF algorithm, sorting, isStrictlyMonotonic) which doesn't allow equal values was added.) + $(LI $(REF readLink, std,file) and $(REF symlink, std,file) have been + rangified.) ) $(BUGSTITLE Library Changes, diff --git a/std/file.d b/std/file.d index ccb82ba7d38..3c7b38ee7e3 100644 --- a/std/file.d +++ b/std/file.d @@ -2310,15 +2310,31 @@ unittest $(D FileException) on error (which includes if the _symlink already exists). +/ -version(StdDdoc) void symlink(C1, C2)(const(C1)[] original, const(C2)[] link) @safe; -else version(Posix) void symlink(C1, C2)(const(C1)[] original, const(C2)[] link) @safe -{ - static auto trustedSymlink(const(C1)[] path1, const(C2)[] path2) @trusted +version(StdDdoc) void symlink(RO, RL)(RO original, RL link) + if ((isInputRange!RO && isSomeChar!(ElementEncodingType!RO) || + isConvertibleToString!RO) && + (isInputRange!RL && isSomeChar!(ElementEncodingType!RL) || + isConvertibleToString!RL)); +else version(Posix) void symlink(RO, RL)(RO original, RL link) + if ((isInputRange!RO && isSomeChar!(ElementEncodingType!RO) || + isConvertibleToString!RO) && + (isInputRange!RL && isSomeChar!(ElementEncodingType!RL) || + isConvertibleToString!RL)) +{ + static if (isConvertibleToString!RO || isConvertibleToString!RL) + { + import std.meta : staticMap; + alias Types = staticMap!(convertToString, RO, RL); + symlink!Types(original, link); + } + else { - return core.sys.posix.unistd.symlink(path1.tempCString(), - path2.tempCString()); + auto oz = original.tempCString(); + auto lz = link.tempCString(); + alias posixSymlink = core.sys.posix.unistd.symlink; + immutable int result = () @trusted { return posixSymlink(oz, lz); } (); + cenforce(result == 0, text(link)); } - cenforce(trustedSymlink(original, link) == 0, link); } version(Posix) @safe unittest @@ -2364,6 +2380,12 @@ version(Posix) @safe unittest } } +version(Posix) unittest +{ + static assert(__traits(compiles, + symlink(TestAliasedString(null), TestAliasedString(null)))); +} + /++ $(BLUE This function is Posix-Only.) @@ -2376,44 +2398,55 @@ version(Posix) @safe unittest Throws: $(D FileException) on error. +/ -version(StdDdoc) string readLink(C)(const(C)[] link) @safe; -else version(Posix) string readLink(C)(const(C)[] link) @safe -{ - static auto trustedReadlink(const(C)[] path, char[] buf) @trusted +version(StdDdoc) string readLink(R)(R link) + if (isInputRange!R && isSomeChar!(ElementEncodingType!R) || + isConvertibleToString!R); +else version(Posix) string readLink(R)(R link) + if (isInputRange!R && isSomeChar!(ElementEncodingType!R) || + isConvertibleToString!R) +{ + static if (isConvertibleToString!R) { - return core.sys.posix.unistd.readlink(path.tempCString(), buf.ptr, buf.length); + return readLink!(convertToString!R)(link); } - static auto trustedAssumeUnique(ref C[] array) @trusted + else { - return assumeUnique(array); - } - - enum bufferLen = 2048; - enum maxCodeUnits = 6; - char[bufferLen] buffer; - auto size = trustedReadlink(link, buffer); - cenforce(size != -1, link); - - if(size <= bufferLen - maxCodeUnits) - return to!string(buffer[0 .. size]); - - auto dynamicBuffer = new char[](bufferLen * 3 / 2); + alias posixReadlink = core.sys.posix.unistd.readlink; + enum bufferLen = 2048; + enum maxCodeUnits = 6; + char[bufferLen] buffer; + const linkz = link.tempCString(); + auto size = () @trusted { + return posixReadlink(linkz, buffer.ptr, buffer.length); + } (); + cenforce(size != -1, to!string(link)); + + if(size <= bufferLen - maxCodeUnits) + return to!string(buffer[0 .. size]); + + auto dynamicBuffer = new char[](bufferLen * 3 / 2); + + foreach(i; 0 .. 10) + { + size = () @trusted { + return posixReadlink(linkz, dynamicBuffer.ptr, + dynamicBuffer.length); + } (); + cenforce(size != -1, to!string(link)); - foreach(i; 0 .. 10) - { - size = trustedReadlink(link, dynamicBuffer); - cenforce(size != -1, link); + if(size <= dynamicBuffer.length - maxCodeUnits) + { + dynamicBuffer.length = size; + return () @trusted { + return assumeUnique(dynamicBuffer); + } (); + } - if(size <= dynamicBuffer.length - maxCodeUnits) - { - dynamicBuffer.length = size; - return trustedAssumeUnique(dynamicBuffer); + dynamicBuffer.length = dynamicBuffer.length * 3 / 2; } - dynamicBuffer.length = dynamicBuffer.length * 3 / 2; + throw new FileException(to!string(link), "Path is too long to read."); } - - throw new FileException(to!string(link), "Path is too long to read."); } version(Posix) @safe unittest @@ -2434,6 +2467,27 @@ version(Posix) @safe unittest assertThrown!FileException(readLink("/doesnotexist")); } +version(Posix) @safe unittest +{ + static assert(__traits(compiles, readLink(TestAliasedString("foo")))); +} + +version(Posix) unittest // input range of dchars +{ + mkdirRecurse(deleteme); + scope(exit) if(deleteme.exists) rmdirRecurse(deleteme); + write(deleteme ~ "/f", ""); + import std.range.interfaces: InputRange, inputRangeObject; + import std.utf: byChar; + immutable string link = deleteme ~ "/l"; + symlink("f", link); + InputRange!dchar linkr = inputRangeObject(link); + alias R = typeof(linkr); + static assert(isInputRange!R); + static assert(!isForwardRange!R); + assert(readLink(linkr) == "f"); +} + /**************************************************** * Get the current working directory.