1 //          Copyright Ferdinand Majeatabase/D/rech 2011 - 2012.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 
7 // Utility functions used by D:GameVFS.
8 module dgamevfs.util;
9 
10 
11 import std.algorithm;
12 import std.array;
13 import std.path;
14 
15 import dgamevfs.exceptions;
16 
17 
18 // Is a file/directory name valid (i.e. no directory or package separators)?
19 bool noSeparators(string name) @trusted pure nothrow @nogc
20 {
21     return !name.canFind("/") && !name.canFind("::");
22 }
23 
24 // Are there no package separators in the path?
25 bool noPackageSeparators(string path) @trusted pure nothrow @nogc
26 {
27     return !path.canFind("::");
28 }
29 
30 // Clean any leading "./" and trailing "/" from a filesystem path, and replace "\" by "/".
31 string cleanFSPath(string path) @trusted pure nothrow
32 {
33     while(path.startsWith("./")) { path = path[2 .. $]; }
34     while(path.endsWith("/"))    { path = path[0 .. $ - 1]; }
35     return path.replace("\\", "/");
36 }
37 
38 /*
39  * If a path starts by a package, return it.
40  *
41  * Params:  path = Path to parse.
42  *          rest = Rest of the path (beyond the package separator)
43  *                 will be written here if the path starts by a package.
44  *                 Otherwise this will be empty.
45  *
46  * Returns: Package the path starts with, if any. null otherwise.
47  */
48 string expectPackage(string path, out string rest) @trusted pure nothrow @nogc
49 {
50     auto parts = path.findSplit("::");
51     // No package separator.
52     if(parts[2].length == 0)  { return null; }
53     // Package separator, but in a subdir.
54     if(parts[0].canFind("/")) { return null; }
55 
56     rest = parts[2];
57     return parts[0];
58 }
59 
60 /*
61  * If a path starts by a subdirectory, return it.
62  *
63  * Params:  path = Path to parse.
64  *          rest = Rest of the path (beyond the directory separator)
65  *                 will be written here if the path starts by a subdirectory.
66  *                 Otherwise this will be empty.
67  *
68  * Returns: Subdirectory the path starts with, if any. null otherwise.
69  *
70  * Throws:  VFSInvalidPathException if a package separator is found in the directory name.
71  */
72 string expectSubdir(string path, out string rest) @trusted pure
73 {
74     auto parts = path.findSplit("/");
75     // No directory separator.
76     if(parts[2].length == 0) { return null; }
77     // Package separator in a directory name.
78     if(parts[0].canFind("::"))
79     {
80         throw invalidPath("Unexpected package separator found in path: ", path);
81     }
82 
83     rest = parts[2];
84     return parts[0];
85 }
86 
87 
88 /**
89  * Match path of a directory or a file relative to a parent directory with
90  * a glob pattern.
91  *
92  * Params:  path       = Path of the file/directory.
93  *          parentPath = Path of the parent directory.
94  *          glob       = Glob pattern to match with. If null, a match is assumed.
95  *
96  * Returns: True on match or if glob is null; false otherwise.
97  */
98 bool subPathMatch(string path, string parentPath, string glob) @safe pure
99 {
100     if(glob is null) { return true; }
101     auto relative = path;
102     relative.skipOver(parentPath);
103     relative.skipOver("/");
104     return globMatch(relative, glob);
105 }