3d91dda86ff75aa59b6095ae6bcab96d72f99656
jcasper
  Wed Mar 18 22:45:15 2026 -0700
eatExcessDotDotInPath does unexpected things with inputs like ../../ and
/a/./.. .  Adding an alternate version until we can assess the impacts of changing.  refs #36320, #37263

diff --git src/lib/filePath.c src/lib/filePath.c
index f4651f8033c..111874ed9ea 100644
--- src/lib/filePath.c
+++ src/lib/filePath.c
@@ -1,22 +1,24 @@
 /* filePath - stuff to handle file name parsing. */
 
 /* Copyright (C) 2011 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "filePath.h"
+#include "portable.h"
+#include "net.h"
 
 
 void undosPath(char *path)
 /* Convert '\' to '/' in path. */
 {
 subChar(path, '\\', '/');
 }
 
 void splitPath(char *path, char dir[PATH_LEN], char name[FILENAME_LEN],
 	       char extension[FILEEXT_LEN])
 /* Split a full path into components.  The dir component will include the
  * trailing / if any.  The extension component will include the starting
  * . if any.   Pass in NULL for dir, name, or extension if you don't care about
  * that part. */
 {
@@ -223,15 +225,38 @@
 char *p = tmpPath;
 while (TRUE)
     {
     char *end = strchr(p, '/');
     if (end != NULL)
         *end = '\0';
     if (sameString(p, ".."))
         return FALSE;
     if (end == NULL)
         break;
     else
         p = end++;
     }
 return TRUE;
 }
+
+char *resolveDotDots(char *pathOrUrl)
+/* Given a file path or URL, return a version with ".." components resolved.
+ * For URLs, only the path portion is simplified (scheme :// is preserved).
+ * Double slashes are collapsed for file paths but not for URLs.
+ * Result should be freeMem'd. */
+{
+if (hasProtocol(pathOrUrl))
+    {
+    struct netParsedUrl npu;
+    netParseUrl(pathOrUrl, &npu);
+    eatExcessDotsInPath(npu.file);
+    return urlFromNetParsedUrl(&npu);
+    }
+else
+    {
+    char result[PATH_LEN];
+    safecpy(result, sizeof(result), pathOrUrl);
+    eatSlashSlashInPath(result);
+    eatExcessDotsInPath(result);
+    return cloneString(result);
+    }
+}