Move sanitary_path_copy() to path.c and rename it to normalize_path_copy()
[git/git.git] / path.c
diff --git a/path.c b/path.c
index a074aea..820eab5 100644 (file)
--- a/path.c
+++ b/path.c
@@ -415,6 +415,92 @@ int normalize_absolute_path(char *buf, const char *path)
        return dst - buf;
 }
 
+int normalize_path_copy(char *dst, const char *src)
+{
+       char *dst0;
+
+       if (has_dos_drive_prefix(src)) {
+               *dst++ = *src++;
+               *dst++ = *src++;
+       }
+       dst0 = dst;
+
+       if (is_dir_sep(*src)) {
+               *dst++ = '/';
+               while (is_dir_sep(*src))
+                       src++;
+       }
+
+       for (;;) {
+               char c = *src;
+
+               /*
+                * A path component that begins with . could be
+                * special:
+                * (1) "." and ends   -- ignore and terminate.
+                * (2) "./"           -- ignore them, eat slash and continue.
+                * (3) ".." and ends  -- strip one and terminate.
+                * (4) "../"          -- strip one, eat slash and continue.
+                */
+               if (c == '.') {
+                       if (!src[1]) {
+                               /* (1) */
+                               src++;
+                       } else if (is_dir_sep(src[1])) {
+                               /* (2) */
+                               src += 2;
+                               while (is_dir_sep(*src))
+                                       src++;
+                               continue;
+                       } else if (src[1] == '.') {
+                               if (!src[2]) {
+                                       /* (3) */
+                                       src += 2;
+                                       goto up_one;
+                               } else if (is_dir_sep(src[2])) {
+                                       /* (4) */
+                                       src += 3;
+                                       while (is_dir_sep(*src))
+                                               src++;
+                                       goto up_one;
+                               }
+                       }
+               }
+
+               /* copy up to the next '/', and eat all '/' */
+               while ((c = *src++) != '\0' && !is_dir_sep(c))
+                       *dst++ = c;
+               if (is_dir_sep(c)) {
+                       *dst++ = '/';
+                       while (is_dir_sep(c))
+                               c = *src++;
+                       src--;
+               } else if (!c)
+                       break;
+               continue;
+
+       up_one:
+               /*
+                * dst0..dst is prefix portion, and dst[-1] is '/';
+                * go up one level.
+                */
+               dst -= 2; /* go past trailing '/' if any */
+               if (dst < dst0)
+                       return -1;
+               while (1) {
+                       if (dst <= dst0)
+                               break;
+                       c = *dst--;
+                       if (c == '/') { /* MinGW: cannot be '\\' anymore */
+                               dst += 2;
+                               break;
+                       }
+               }
+       }
+       *dst = '\0';
+       return 0;
+}
+
 /*
  * path = Canonical absolute path
  * prefix_list = Colon-separated list of absolute paths