Skip to content

Instantly share code, notes, and snippets.

@rikkimax
Created August 25, 2015 06:40
Show Gist options
  • Save rikkimax/e15984513b57714364f3 to your computer and use it in GitHub Desktop.
Save rikkimax/e15984513b57714364f3 to your computer and use it in GitHub Desktop.
module dnetdev.vfs.path;
import std.experimental.allocator : IAllocator, makeArray, theAllocator, expandArray, shrinkArray, dispose, make;
private {
// santisization uses this
char[1024] charBuffer;
size_t[128] charIndexBuffer;
char[1024] tCopyBuffer;
}
/**
* Does not handle the encoding/decoding
*/
struct URIAddress {
string value;
alias value this;
private {
IAllocator alloc;
char[][] arrbuffDirectories;
}
~this() {
alloc.dispose(cast(char[])value);
alloc.dispose(arrbuffDirectories);
}
/**
* Takes a string that represents a URI path
*
* Rerpresents a URI in the form of "<protName:[(//)(\\\\)]>[user[:pass]]@[host/ip[:port]][/][<dir/>...][file[.ext]]".
* Will automatically sanitise the input after duplicating it.
*
* Can be used to represent a file system entry (such as a Windows drive). But will be sanitised out as forward slash.
*
* Params:
* value = The path value, defaults to current working directory.
* alloc = The allocator to allocate return values as necssary.
*/
this(string value=".", IAllocator alloc=theAllocator()) {
this.alloc = alloc;
arrbuffDirectories = alloc.makeArray!(char[])(0);
// duplicate the value
this.value = cast(immutable)alloc.makeArray!char(value.length);
(cast(char[])this.value[]) = cast(char[])value[];
// sanitise the value
sanitise();
}
@property {
///
IAllocator allocator() { return alloc; }
///
string connectionInfo() {
import std.string : indexOf;
// <protName://> <driveLetter:\\>
string prot = URIProtocolPrefix(value);
if (prot.length <= 1)
return null;
string value2 = value[prot.length + 3 .. $];
// [user[:pass]]@[host/ip[:port]][/][<dir/>...][file[.ext]]
ptrdiff_t i;
if ((i = value2.indexOf("/")) >= 0) {
return value[0 .. i + prot.length + 3];
} else {
return value;
}
}
///
string protocol() {
string prot = URIProtocolPrefix(value);
string auth = URIAuthentication(value);
if (prot.length == 1 && auth is null)
return null;
else
return prot;
}
///
string driveLetter() {
string prot = URIProtocolPrefix(value);
string auth = URIAuthentication(value);
if (prot.length == 1 && auth is null)
return prot;
else
return null;
}
///
string username() {
import std.string : indexOf;
string prefix = URIAuthentication(value);
ptrdiff_t i;
if ((i = prefix.indexOf(":")) > 0) {
return prefix[0 .. i];
} else if (i == 0) {
return null;
} else {
return prefix;
}
}
///
string password() {
import std.string : indexOf;
string prefix = URIAuthentication(value);
ptrdiff_t i;
if ((i = prefix.indexOf(":")) >= 0)
if (prefix.length - (i + 1) != 0)
return prefix[i + 1 .. $];
return null;
}
///
ushort port() {
import std.string : indexOf;
import std.conv : to;
string prefix = URIConnection(value);
ptrdiff_t i;
if ((i = prefix.indexOf(":")) > 0 && prefix.length - i > 1) {
return to!ushort(prefix[i + 1 .. $]);
} else {
return 0;
}
}
///
immutable(string[]) parts() {
size_t numDir;
string addr = URIEntries(value);
if (addr.length > 0) {
foreach(i, c; addr) {
if (i > 0 && i < addr.length-1 && c == '/') {
numDir++;
}
}
if (addr[$-1] != '/')
numDir++;
}
if (arrbuffDirectories.length < numDir)
alloc.expandArray(arrbuffDirectories, numDir - arrbuffDirectories.length);
string t;
size_t numDirI;
size_t lastI;
if (addr.length > 0 && addr[0] == '/')
lastI = 1;
foreach(i, c; addr) {
if (c == '/' && i > 0) {
arrbuffDirectories[numDirI] = cast(char[])t;
numDirI++;
t = null;
lastI = i + 1;
} else {
t = addr[lastI .. i + 1];
}
}
if (t !is null) {
arrbuffDirectories[numDirI] = cast(char[])t;
}
return cast(immutable(string[]))arrbuffDirectories[0 .. numDir];
}
immutable(string[]) partsStairCase(bool reverse=false) {
import std.string : indexOf;
size_t startI;
string addr = URIEntries(value);
string[] ret = cast(string[])parts();
foreach(i, part; ret) {
startI += addr[startI .. $].indexOf(part) + part.length;
ret[i] = addr[0 .. startI];
}
if (reverse) {
foreach(i1; 0 .. ret.length / 2) {
size_t i2 = ret.length - (i1 + 1);
if (i2 != i1) {
string v1 = ret[i1];
string v2 = ret[i2];
ret[i2] = v1;
ret[i1] = v2;
}
}
}
return cast(immutable)ret;
}
}
///
void sanitise() {
import std.uni : toUpper;
char[] temp = cast(char[])value;
// make sure first directory entry starts with either a drive, root(/), . if not a protocol/drive or ~
if (URIConnection(value) is null) {
if (driveLetter !is null) {
temp[0] = cast(char)toUpper(temp[0]);
temp[2] = '\\';
temp[3] = '\\';
} else if (temp.length > 0 && temp[0] == '~') {
if (temp.length > 1 && !(temp[1] == '/' || temp[1] == '\\')) {
char[] temp2 = alloc.makeArray!char(temp.length + 1);
temp2[0] = temp[0];
temp2[1] = '/';
temp2[2 .. $] = temp[1 .. $];
alloc.dispose(temp);
temp = temp2;
value = cast(immutable)temp;
}
} else if (temp.length == 0) {
alloc.expandArray(temp, 1);
temp[0] = '/';
}
} else if (URIProtocolPrefix(value) !is null) {
size_t offset = URIProtocolPrefix(value).length;
temp[offset + 1] = '/';
temp[offset + 2] = '/';
}
// make directory seperator only /
foreach(ref c; cast(char[])URIEntries(value)) {
if (c == '\\')
c = '/';
}
// remove last / if added
if (temp[$-1] == '/' && temp.length > 1)
alloc.shrinkArray(temp, 1);
// work around, should not be needed
value = cast(immutable)temp;
size_t dashes;
size_t i = URIProtocolPrefix(value).length;
if (i == 1 && temp.length > 4 && temp[4] == '/') {
char[] temp2 = charBuffer[0 .. temp.length - 5];
temp2[] = temp[5 .. $];
temp[4 .. temp2.length+4] = temp2[];
temp = temp[0 .. temp2.length+4];
}
if (i > 0)
i += 3; // <prot>://
for(;;) {
if (i >= temp.length)
break;
if (temp[i] == '/') {
i++;
dashes++;
} else if (dashes > 1) {
char[] temp2 = charBuffer[0 .. temp.length - (dashes-1)];
temp2[0 .. i - dashes] = temp[0 .. i - dashes];
temp2[i-dashes .. $] = temp[i-(dashes-1) .. $];
temp[0 .. temp2.length] = temp2[];
temp = temp[0 .. temp2.length];
i -= (dashes-1);
//i++;
dashes = 0;
} else {
i++;
dashes = 0;
}
}
// work around, should not be needed
value = cast(immutable)temp;
}
///
URIAddress expand(URIAddress homeDirectory, URIAddress cwd) {
import std.string : indexOf;
string prot = URIProtocolPrefix(value);
string temp = value;
char[] retbuf;
size_t offsetDirs;
// grabs <protName://>
if (prot.length > 1) {
ptrdiff_t offseti;
retbuf = charBuffer[0 .. prot.length + 3];
retbuf[0 .. prot.length] = prot[0 .. 3];
retbuf[prot.length .. $] = "://";
temp = temp[prot.length + 3 .. $];
offseti = temp.indexOf("/");
// assuming sanitised correctly then this will /always/ exist
assert(offseti >= 0);
// grabs [user[:pass]]@[host/ip[:port]]
retbuf = charBuffer[0 .. retbuf.length + offseti];
retbuf[$-offseti .. $] = temp[0 .. offseti];
temp = temp[offseti .. $];
offsetDirs = retbuf.length;
}
if (temp[0] == '/')
temp = temp[1 .. $];
if (temp[0] == '.' || temp[0] == '~') {
string replaceTo;
// perform expansion
if (temp[0] == '.') {
if (URIProtocolPrefix(cwd) !is null && prot.length > 0)
throw new Exception("Protocol/drive letter as directory cannot be used in expansion");
if (cwd.driveLetter !is null) {
replaceTo = cwd.value;
offsetDirs = 4;
} else {
replaceTo = URIEntries(cwd);
}
} else if (temp[0] == '~') {
if (URIProtocolPrefix(homeDirectory) !is null && prot.length > 0)
throw new Exception("Protocol/drive letter as directory cannot be used in expansion");
if (homeDirectory.driveLetter !is null) {
replaceTo = homeDirectory.value;
offsetDirs = 4;
} else {
replaceTo = URIEntries(homeDirectory);
}
}
if ((replaceTo.length > 0 && replaceTo[0] == '/') || retbuf.length == 0) {
retbuf = charBuffer[0 .. retbuf.length + replaceTo.length];
} else {
retbuf = charBuffer[0 .. retbuf.length + replaceTo.length + 1];
retbuf[$-(replaceTo.length+1)] = '/';
}
retbuf[$-replaceTo.length .. $] = replaceTo[];
temp = temp[1 .. $];
}
if (temp.length > 0 && temp[0] == '/') {
retbuf = charBuffer[0 .. retbuf.length + temp.length];
} else {
retbuf = charBuffer[0 .. retbuf.length + (temp.length + 1)];
retbuf[$-(temp.length + 1)] = '/';
}
retbuf[$-temp.length .. $] = temp[];
// expansion of parent and current directory is simple.
// current directory gets removed out right.
// parent directory removes it and its parent node if it should exist.
// C://dir1/dir2/../../dir3 C://dir3
URIAddress* addrt = alloc.make!URIAddress(cast(string)retbuf, alloc);
retbuf = charBuffer[0 .. addrt.value.length];
retbuf[] = addrt.value[];
size_t skipDirs;
auto parts = addrt.parts;
size_t[] ibuffers;
foreach_reverse(i, p; parts) {
if (p == ".") continue;
else if (p == "..") {
skipDirs++;
continue;
} else if (skipDirs > 0) {
skipDirs--;
continue;
}
ibuffers = charIndexBuffer[0 .. ibuffers.length + 1];
ibuffers[$-1] = i;
}
retbuf = retbuf[0 .. offsetDirs];
// prot://user:pass@server:port/
foreach_reverse(i; ibuffers) {
size_t len = parts[i].length;
retbuf = charBuffer[0 .. retbuf.length + len + 1];
retbuf[$-(len + 1)] = '/';
retbuf[$-len .. $] = parts[i];
}
alloc.dispose(addrt);
try {
return URIAddress(cast(string)retbuf, alloc);
} catch(Error e) {
assert(0, retbuf);
}
}
///
URIAddress parent() {
import std.string : indexOf;
string current = URIEntries(value);
size_t start;
foreach_reverse(i, c; current) {
if (c == '/') {
start = i;
break;
}
}
return URIAddress(value[0 .. value.indexOf(current) + start], alloc);
}
///
URIAddress subPath(URIAddress sub) {
string suff = URIEntries(sub);
char[] buff = tCopyBuffer[0 .. value.length + 1];
buff[0 .. $-1] = value[];
buff[$-1] = '/';
buff = tCopyBuffer[0 .. buff.length + suff.length];
buff[$-suff.length .. $] = suff[];
return URIAddress(cast(string)buff, alloc);
}
///
URIAddress sibling(URIAddress subling) {
import std.string : indexOf;
string current = URIEntries(value);
string suff = URIEntries(subling);
size_t start;
foreach_reverse(i, c; current) {
if (c == '/') {
start = i;
break;
}
}
string value2 = value[0 .. value.indexOf(current) + start];
char[] buff = tCopyBuffer[0 .. value2.length + 1];
buff[0 .. $-1] = value2[];
buff[$-1] = '/';
buff = tCopyBuffer[0 .. buff.length + suff.length];
buff[$-suff.length .. $] = suff[];
return URIAddress(cast(string)buff, alloc);
}
}
///
unittest {
assert(URIAddress("").connectionInfo is null);
assert(URIAddress("/abc").connectionInfo is null);
assert(URIAddress("/abc/def").connectionInfo is null);
assert(URIAddress("c://test.txt").connectionInfo is null);
assert(URIAddress("smb://test").connectionInfo == "smb://test");
assert(URIAddress("smb://test/what").connectionInfo == "smb://test");
assert(URIAddress("smb://user@test").connectionInfo == "smb://user@test");
assert(URIAddress("smb://user:pass@test").connectionInfo == "smb://user:pass@test");
assert(URIAddress("smb://user:pass@test/dir1/dir2").connectionInfo == "smb://user:pass@test");
}
///
unittest {
assert(URIAddress("").value == "/");
assert(URIAddress("smb:\\\\test/what") == "smb://test/what");
assert(URIAddress("smb://test/what") == "smb://test/what");
assert(URIAddress("~/test.txt") == "~/test.txt");
assert(URIAddress("~\\test.txt") == "~/test.txt");
assert(URIAddress("~test.txt") == "~/test.txt");
assert(URIAddress("c:\\\\test.txt") == "C:\\\\test.txt");
assert(URIAddress("c://test.txt") == "C:\\\\test.txt");
assert(URIAddress("/test\\abc") == "/test/abc");
assert(URIAddress("/") == "/");
assert(URIAddress("/test/") == "/test");
}
///
unittest {
assert(URIAddress("smb://:mypass@server").username is null);
assert(URIAddress("smb://test@server").username == "test");
assert(URIAddress("smb://test:mypass@server").username == "test");
assert(URIAddress("smb://test:@server").username == "test");
assert(URIAddress("smb://:mypass@server").password == "mypass");
assert(URIAddress("smb://test@server").password is null);
assert(URIAddress("smb://test:mypass@server").password == "mypass");
assert(URIAddress("smb://test:@server").password is null);
}
///
unittest {
assert(URIAddress("smb://server:89").port == 89);
assert(URIAddress("smb://server:").port == 0);
assert(URIAddress("smb://server").port == 0);
}
///
unittest {
URIAddress addr = URIAddress("/dir1/file.txt");
assert(addr.parts.length == 2);
assert(addr.parts[0] == "dir1");
assert(addr.parts[1] == "file.txt");
addr.value = "/dir1/dir2/file.txt";
addr.sanitise();
assert(addr.parts.length == 3);
assert(addr.parts[0] == "dir1");
assert(addr.parts[1] == "dir2");
assert(addr.parts[2] == "file.txt");
addr.value = "/dir1/file.txt";
addr.sanitise();
assert(addr.parts.length == 2);
assert(addr.parts[0] == "dir1");
assert(addr.parts[1] == "file.txt");
assert(addr.arrbuffDirectories.length == 3);
}
///
unittest {
URIAddress addr = URIAddress("smb://server/dir1/file.txt");
assert(addr.parts.length == 2);
assert(addr.parts[0] == "dir1");
assert(addr.parts[1] == "file.txt");
}
///
unittest {
URIAddress addr = URIAddress("/dir1/dir2/file.ext");
immutable(string[]) parts = addr.partsStairCase;
assert(parts.length == 3);
assert(parts[0] == "/dir1");
assert(parts[1] == "/dir1/dir2");
assert(parts[2] == "/dir1/dir2/file.ext");
}
///
unittest {
URIAddress addr = URIAddress("/dir1/dir2/file.ext");
immutable(string[]) parts = addr.partsStairCase(true);
assert(parts.length == 3);
assert(parts[0] == "/dir1/dir2/file.ext");
assert(parts[1] == "/dir1/dir2");
assert(parts[2] == "/dir1");
}
///
unittest {
assert(URIAddress("./file.ext") == "./file.ext");
assert(URIAddress(".file.ext") == ".file.ext");
assert(URIAddress("~/file.ext") == "~/file.ext");
assert(URIAddress("~file.ext") == "~/file.ext");
}
///
unittest {
assert(URIAddress("/dir1//dir2/file.ext") == "/dir1/dir2/file.ext");
assert(URIAddress("C://dir1//dir2/file.ext") == "C:\\\\dir1/dir2/file.ext");
assert(URIAddress("C:\\\\/dir1//dir2/file.ext") == "C:\\\\dir1/dir2/file.ext");
}
///
unittest {
assert(URIAddress("./test/file.ext").expand(URIAddress("/"), URIAddress("C:\\\\")) == "C:\\\\test/file.ext");
assert(URIAddress("./test/file.ext").expand(URIAddress("/"), URIAddress("/etc")) == "/etc/test/file.ext");
assert(URIAddress("~/test/file.ext").expand(URIAddress("C:\\\\"), URIAddress("/")) == "C:\\\\test/file.ext");
assert(URIAddress("~/test/file.ext").expand(URIAddress("/etc"), URIAddress("/")) == "/etc/test/file.ext");
assert(URIAddress("./test/file.ext").expand(URIAddress("C:\\\\"), URIAddress("/")) == "/test/file.ext");
assert(URIAddress("./test/file.ext").expand(URIAddress("/etc"), URIAddress("/")) == "/test/file.ext");
assert(URIAddress("~/test/file.ext").expand(URIAddress("/"), URIAddress("C:\\\\")) == "/test/file.ext");
assert(URIAddress("~/test/file.ext").expand(URIAddress("/"), URIAddress("/etc")) == "/test/file.ext");
}
///
unittest {
assert(URIAddress("./dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("C:\\\\")) == "C:\\\\dir3");
assert(URIAddress("./dir1/dir2/../../dir3").expand(URIAddress("C:\\\\"), URIAddress("/")) == "/dir3");
assert(URIAddress("~/dir1/dir2/../../dir3").expand(URIAddress("C:\\\\"), URIAddress("/")) == "C:\\\\dir3");
assert(URIAddress("~/dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("C:\\\\")) == "/dir3");
}
///
unittest {
assert(URIAddress("smb://server/./test").expand(URIAddress("/"), URIAddress("/")) == "smb://server/test");
assert(URIAddress("smb://server/test").expand(URIAddress("/"), URIAddress("/")) == "smb://server/test");
assert(URIAddress("smb://server/~/test").expand(URIAddress("/etc"), URIAddress("/")) == "smb://server/etc/test");
assert(URIAddress("smb://server/./test").expand(URIAddress("/"), URIAddress("/etc")) == "smb://server/etc/test");
assert(URIAddress("smb://server/dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("/")) == "smb://server/dir3");
}
///
unittest {
try {
auto got = URIAddress("smb://server/~").expand(URIAddress("C:\\\\"), URIAddress("/"));
assert(0);
} catch (Exception e) {
assert(1);
}
try {
auto got = URIAddress("smb://server/.").expand(URIAddress("/"), URIAddress("C:\\\\"));
assert(0);
} catch (Exception e) {
assert(1);
}
}
///
unittest {
assert(URIAddress("./dir1/dir2").parent == "./dir1");
assert(URIAddress("~/dir1/dir2").parent == "~/dir1");
assert(URIAddress("C:\\\\dir1/dir2").parent == "C:\\\\dir1");
assert(URIAddress("smb://server/dir1/dir2").parent == "smb://server/dir1");
assert(URIAddress("smb://server/dir1").parent == "smb://server");
}
///
unittest {
assert(URIAddress("/etc/dir1/").subPath(URIAddress("dir2")) == "/etc/dir1/dir2");
assert(URIAddress("/etc/dir1/").subPath(URIAddress("../dir2")) == "/etc/dir1/../dir2");
}
unittest {
assert(URIAddress("/etc/dir1/").sibling(URIAddress("dir2")) == "/etc/dir2");
assert(URIAddress("/etc/dir1/").sibling(URIAddress("../dir2")) == "/etc/../dir2");
assert(URIAddress("/etc/dir1/").sibling(URIAddress("dir2/file.ext")) == "/etc/dir2/file.ext");
}
@safe /+@nogc+/:
///
string URIProtocolPrefix(string value) {
bool melformed;
scope(exit)
assert(!melformed);
return URIProtocolPrefix(value, melformed);
}
///
string URIProtocolPrefix(string value, out bool melformed) {
import std.string : indexOf;
melformed = false;
ptrdiff_t i;
if ((i = value.indexOf("://")) > 0) {
return value[0 .. i];
} else if ((i = value.indexOf(":\\\\")) > 0) {
return value[0 .. i];
} else if ((i = value.indexOf(":/")) > 0) {
melformed = true;
return null;
} else if ((i = value.indexOf(":\\")) > 0) {
melformed = true;
return null;
} else
return null;
}
///
unittest {
bool melformed;
assert(URIProtocolPrefix("abc", melformed) is null);
assert(!melformed);
assert(URIProtocolPrefix("ab", melformed) is null);
assert(!melformed);
assert(URIProtocolPrefix("a/b/c.d", melformed) is null);
assert(!melformed);
assert(URIProtocolPrefix("d:/", melformed) is null);
assert(melformed);
assert(URIProtocolPrefix("d://", melformed) == "d");
assert(!melformed);
assert(URIProtocolPrefix("d://a/b.c", melformed) == "d");
assert(!melformed);
assert(URIProtocolPrefix("D://a/b.c", melformed) == "D");
assert(!melformed);
assert(URIProtocolPrefix("a\\b\\c.d", melformed) is null);
assert(!melformed);
assert(URIProtocolPrefix("d:\\", melformed) is null);
assert(melformed);
assert(URIProtocolPrefix("d:\\\\", melformed) == "d");
assert(!melformed);
assert(URIProtocolPrefix("d:\\\\a\\b.c", melformed) == "d");
assert(!melformed);
assert(URIProtocolPrefix("D:\\\\a\\b.c", melformed) == "D");
assert(!melformed);
}
///
string URIProtocolSuffix(string value) {
import std.string : indexOf;
if (value.length < 3)
return null;
ptrdiff_t i;
if ((i = value.indexOf("://")) > 0) {
if (value.length > i + 3)
return value[i + 3 .. $];
else
return null;
} else if ((i = value.indexOf(":\\\\")) > 0) {
if (value.length > i + 3)
return value[i + 3 .. $];
else
return null;
} else
return value;
}
///
unittest {
assert(URIProtocolSuffix("a/b/c") == "a/b/c");
assert(URIProtocolSuffix("a\\b\\c") == "a\\b\\c");
assert(URIProtocolSuffix("a://b/c") == "b/c");
assert(URIProtocolSuffix("a:\\\\b\\c") == "b\\c");
}
///
string URIAuthentication(string value) {
import std.string : indexOf;
string input = URIProtocolSuffix(value);
ptrdiff_t i;
if ((i = input.indexOf("@")) > 0) {
return input[0 .. i];
} else {
return null;
}
}
///
unittest {
assert(URIAuthentication("a/b/c") is null);
assert(URIAuthentication("a@b/c") == "a");
assert(URIAuthentication("a:b@c/d") == "a:b");
}
///
string URIAuthenticationSuffix(string value) {
import std.string : indexOf;
string input = URIProtocolSuffix(value);
ptrdiff_t i;
if ((i = input.indexOf("@")) > 0 && input.length - i > 1) {
return input[i + 1 .. $];
} else {
return input;
}
}
///
unittest {
assert(URIAuthenticationSuffix("a/b/c") == "a/b/c");
assert(URIAuthenticationSuffix("a@b/c") == "b/c");
assert(URIAuthenticationSuffix("a:b@c/d") == "c/d");
}
///
string URIConnection(string value) {
import std.string : indexOf;
string prot = URIProtocolPrefix(value);
string auth = URIAuthentication(value);
if (prot.length == 1 && auth is null)
return null;
else if (prot.length > 1) {
ptrdiff_t i;
string input = URIAuthenticationSuffix(value);
if ((i = input.indexOf("/")) > 0) {
return input[0 .. i];
} else if ((i = input.indexOf("\\")) > 0) {
return input[0 .. i];
} else {
return input;
}
} else {
return null;
}
}
///
unittest {
assert(URIConnection("") is null);
assert(URIConnection("d://abc") is null);
assert(URIConnection("d:\\\\abc") is null);
assert(URIConnection("/etc/something.txt") is null);
assert(URIConnection("smb://abc/d/f") == "abc");
assert(URIConnection("smb:\\\\abc\\d\\f") == "abc");
assert(URIConnection("smb://abc") == "abc");
}
///
string URIQuery(string value) {
import std.string : indexOf;
string input = URIAuthenticationSuffix(value);
ptrdiff_t i;
if ((i = input.indexOf("?")) > 0 && input.length - i > 1) {
return input[i + 1 .. $];
} else {
return null;
}
}
unittest {
assert(URIQuery("abc/d?b=e&z=y") == "b=e&z=y");
assert(URIQuery("smb://abc/d?b=e&z=y") == "b=e&z=y");
assert(URIQuery("/") is null);
assert(URIQuery("/?") is null);
}
///
string URIEntries(string value) {
import std.string : indexOf;
string input = URIAuthenticationSuffix(value);
string prot = URIProtocolPrefix(value);
string query = URIQuery(input);
if (query !is null)
input = input[0 .. $-(query.length + 1)];
ptrdiff_t i;
if (prot.length > 1 && (i = input.indexOf("/")) > 0 && input.length - i > 1) {
return input[i + 1 .. $];
} else if (prot.length > 1 && (i = input.indexOf("\\")) > 0 && input.length - i > 1) {
return input[i + 1 .. $];
} else if (prot.length > 1) {
return null;
} else {
return input;
}
}
///
unittest {
assert(URIEntries("d://abc") == "abc");
assert(URIEntries("d:\\\\abc") == "abc");
assert(URIEntries("/etc/something.txt") == "/etc/something.txt");
assert(URIEntries("smb://abc/d/f") == "d/f");
assert(URIEntries("smb:\\\\abc\\d\\f") == "d\\f");
assert(URIEntries("smb://abc") is null);
assert(URIEntries("abc/d?b=e&z=y") == "abc/d");
assert(URIEntries("smb://abc/d?b=e&z=y") == "d");
assert(URIEntries("/") is null);
assert(URIEntries("/?") is null);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment