]> sjero.net Git - wget/commitdiff
[svn] Modified path_simplify not to rely on extensive use of memmove.
authorhniksic <devnull@localhost>
Mon, 22 Sep 2003 00:23:44 +0000 (17:23 -0700)
committerhniksic <devnull@localhost>
Mon, 22 Sep 2003 00:23:44 +0000 (17:23 -0700)
src/ChangeLog
src/url.c

index f619c189429e343c3973272b1836b2bc207ccfb9..6d95e45d162cb6f712cce3feb6d6079dd3ab0ded 100644 (file)
@@ -1,3 +1,8 @@
+2003-09-22  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * url.c (path_simplify): Instead of calls to memmove, handle "./"
+       and "../" by advancing pointers.
+
 2003-09-22  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * retr.c (getproxy): Moved from url.c.
index d27a902c0b82b441feacf5fb7bb259c573999d6d..21005eba5e7bd6b0315005dfc24fd79996ae02c5 100644 (file)
--- a/src/url.c
+++ b/src/url.c
@@ -1612,8 +1612,8 @@ find_last_char (const char *b, const char *e, char c)
 \f
 /* Resolve "." and ".." elements of PATH by destructively modifying
    PATH.  "." is resolved by removing that path element, and ".." is
-   resolved by removing the preceding path element.  Leading and
-   trailing slashes are preserved.
+   resolved by removing the preceding path element.  Single leading
+   and trailing slashes are preserved.
 
    Return non-zero if any changes have been made.
 
@@ -1628,108 +1628,77 @@ find_last_char (const char *b, const char *e, char c)
 static int
 path_simplify (char *path)
 {
-  int change = 0;
-  char *p, *end;
+  char *h, *t, *end;
 
+  /* Preserve the leading '/'. */
   if (path[0] == '/')
-    ++path;                    /* preserve the leading '/'. */
+    ++path;
 
-  p = path;
-  end = p + strlen (p) + 1;    /* position past the terminating zero. */
+  h = path;                    /* hare */
+  t = path;                    /* tortoise */
+  end = path + strlen (path);
 
-  while (1)
+  while (h < end)
     {
-    again:
-      /* P should point to the beginning of a path element. */
+      /* Hare should be at the beginning of a path element. */
 
-      if (*p == '.' && (*(p + 1) == '/' || *(p + 1) == '\0'))
+      if (h[0] == '.' && (h[1] == '/' || h[1] == '\0'))
        {
-         /* Handle "./foo" by moving "foo" two characters to the
-            left. */
-         if (*(p + 1) == '/')
-           {
-             change = 1;
-             memmove (p, p + 2, end - (p + 2));
-             end -= 2;
-             goto again;
-           }
-         else
-           {
-             change = 1;
-             *p = '\0';
-             break;
-           }
+         /* Ignore "./". */
+         h += 2;
        }
-      else if (*p == '.' && *(p + 1) == '.'
-              && (*(p + 2) == '/' || *(p + 2) == '\0'))
+      else if (h[0] == '.' && h[1] == '.' && (h[2] == '/' || h[2] == '\0'))
        {
-         /* Handle "../foo" by moving "foo" one path element to the
-            left.  */
-         char *b = p;          /* not p-1 because P can equal PATH */
-
-         /* Backtrack by one path element, but not past the beginning
-            of PATH. */
-
-         /* foo/bar/../baz */
-         /*         ^ p    */
-         /*     ^ b        */
+         /* Handle "../" by retreating the tortoise by one path
+            element -- but not past beggining of PATH.  */
 
-         if (b > path)
+         if (t > path)
            {
              /* Move backwards until B hits the beginning of the
                 previous path element or the beginning of path. */
-             for (--b; b > path && *(b - 1) != '/'; b--)
+             for (--t; t > path && t[-1] != '/'; t--)
                ;
            }
-
-         change = 1;
-         if (*(p + 2) == '/')
+         h += 3;
+       }
+      else if (*h == '/')
+       {
+         /* Ignore empty path elements.  Supporting them is hard (in
+            which directory do you save http://x.com///y.html?), and
+            they don't bring any practical gain.  Plus, they break
+            our filesystem-influenced assumptions: allowing empty
+            path elements means that "x/y/../z" simplifies to
+            "x/y/z", whereas most people would expect "x/z".  */
+         ++h;
+       }
+      else
+       {
+         /* A regular path element.  If H hasn't advanced past T,
+            simply skip to the next path element.  Otherwise, copy
+            the path element until the next slash.  */
+         if (t == h)
            {
-             memmove (b, p + 3, end - (p + 3));
-             end -= (p + 3) - b;
-             p = b;
+             /* Skip the path element, including the slash.  */
+             while (h < end && *h != '/')
+               t++, h++;
+             if (h < end)
+               t++, h++;
            }
          else
            {
-             *b = '\0';
-             break;
+             /* Copy the path element, including the final slash.  */
+             while (h < end && *h != '/')
+               *t++ = *h++;
+             if (h < end)
+               *t++ = *h++;
            }
-
-         goto again;
        }
-      else if (*p == '/')
-       {
-         /* Remove empty path elements.  Not mandated by rfc1808 et
-            al, but it seems like a good idea to get rid of them.
-            Supporting them properly is hard (in which directory do
-            you save http://x.com///y.html?) and they don't seem to
-            bring much gain.  */
-         char *q = p;
-         while (*q == '/')
-           ++q;
-         change = 1;
-         if (*q == '\0')
-           {
-             *p = '\0';
-             break;
-           }
-         memmove (p, q, end - q);
-         end -= q - p;
-         goto again;
-       }
-
-      /* Skip to the next path element. */
-      while (*p && *p != '/')
-       ++p;
-      if (*p == '\0')
-       break;
-
-      /* Make sure P points to the beginning of the next path element,
-        which is location after the slash. */
-      ++p;
     }
 
-  return change;
+  if (t != h)
+    *t = '\0';
+
+  return t != h;
 }
 \f
 /* Merge BASE with LINK and return the resulting URI.