From a673125fbbf24b3a5abfe397396d5fc32360be09 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Fri, 31 Mar 2017 15:08:16 +0200
Subject: [PATCH] types.*pathname: normalize slashes

---
 lib/types.nix | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/lib/types.nix b/lib/types.nix
index edd48c35b..8a3c76483 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -5,7 +5,7 @@ let
     all any concatMapStringsSep concatStringsSep const filter flip genid
     hasSuffix head isInt isString length match mergeOneOption mkOption
     mkOptionType optional optionalAttrs optionals range splitString
-    stringLength tail typeOf;
+    stringLength substring typeOf;
   inherit (lib.types)
     attrsOf bool either enum int listOf nullOr path str string submodule;
 in
@@ -430,23 +430,23 @@ rec {
   };
 
   # POSIX.1‐2013, 3.2 Absolute Pathname
-  # TODO normalize slashes
-  # TODO two slashes
   absolute-pathname = mkOptionType {
     name = "POSIX absolute pathname";
-    check = x: let xs = splitString "/" x; xa = head xs; in
-         isString x
-      && stringLength x > 0
-      && (xa == "/" || (xa == "" && all filename.check (tail xs)));
+    check = x: isString x && substring 0 1 x == "/" && pathname.check x;
     merge = mergeOneOption;
   };
 
   # POSIX.1‐2013, 3.267 Pathname
-  # TODO normalize slashes
   pathname = mkOptionType {
     name = "POSIX pathname";
-    check = x: let xs = splitString "/" x; in
-      isString x && all filename.check (if head xs == "" then tail xs else xs);
+    check = x:
+      let
+        # The filter is used to normalize paths, i.e. to remove duplicated and
+        # trailing slashes.  It also removes leading slashes, thus we have to
+        # check for "/" explicitly below.
+        xs = filter (s: stringLength s > 0) (splitString "/" x);
+      in
+        isString x && (x == "/" || (length xs > 0 && all filename.check xs));
     merge = mergeOneOption;
   };