about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-07-08T19·58+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-07-08T19·58+0000
commitcab3f4977a412681a77767ec7307ee642b61332d (patch)
tree469274938dd5a59f93dad4f4e8dffa5f27593c80
parent333f4963de6d174d852774a88ada852f77f57994 (diff)
* A path canonicaliser that doesn't depend on the existence of paths
  (i.e., it doesn't use realpath(3), which is broken in any case).
  Therefore it doesn't resolve symlinks.

-rw-r--r--src/test.cc9
-rw-r--r--src/util.cc38
2 files changed, 42 insertions, 5 deletions
diff --git a/src/test.cc b/src/test.cc
index b30a5b0e90b6..c2a1cd3bfd55 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -71,6 +71,15 @@ void runTests()
         abort();
     } catch (BadRefError err) { };
 
+    /* Path canonicalisation. */
+    cout << canonPath("/./../././//") << endl;
+    cout << canonPath("/foo/bar") << endl;
+    cout << canonPath("///foo/////bar//") << endl;
+    cout << canonPath("/././/foo/////bar//.") << endl;
+    cout << canonPath("/foo////bar//..///x/") << endl;
+    cout << canonPath("/foo////bar//..//..//x/y/../z/") << endl;
+    cout << canonPath("/foo/bar/../../../..///") << endl;
+
     /* Dumping. */
 
 #if 0
diff --git a/src/util.cc b/src/util.cc
index 8ccd3c1524d8..00a3063d6d13 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -40,11 +40,39 @@ string absPath(string path, string dir)
 
 string canonPath(const string & path)
 {
-    char resolved[PATH_MAX];
-    if (!realpath(path.c_str(), resolved))
-        throw SysError(format("cannot canonicalise path `%1%'") % path);
-    /* !!! check that this removes trailing slashes */
-    return resolved;
+    string s;
+
+    if (path[0] != '/')
+        throw Error(format("not an absolute path: `%1%'") % path);
+
+    string::const_iterator i = path.begin(), end = path.end();
+
+    while (1) {
+
+        /* Skip slashes. */
+        while (i != end && *i == '/') i++;
+        if (i == end) break;
+
+        /* Ignore `.'. */
+        if (*i == '.' && (i + 1 == end || i[1] == '/'))
+            i++;
+
+        /* If `..', delete the last component. */
+        else if (*i == '.' && i + 1 < end && i[1] == '.' && 
+            (i + 2 == end || i[2] == '/'))
+        {
+            if (!s.empty()) s.erase(s.rfind('/'));
+            i += 2;
+        }
+
+        /* Normal component; copy it. */
+        else {
+            s += '/';
+            while (i != end && *i != '/') s += *i++;
+        }
+    }
+
+    return s.empty() ? "/" : s;
 }