Code

Avoid segfault with 'git branch' when the HEAD is detached
[git.git] / path.c
diff --git a/path.c b/path.c
index 820eab5ac32f6f30062b0c5bc5be89b2eb9bb9ce..dd22370e8e5cb932f9c8ce8756c154073771b35b 100644 (file)
--- a/path.c
+++ b/path.c
@@ -154,7 +154,7 @@ int validate_headref(const char *path)
        /* Make sure it is a "refs/.." symlink */
        if (S_ISLNK(st.st_mode)) {
                len = readlink(path, buffer, sizeof(buffer)-1);
-               if (len >= 5 && !memcmp("refs/", buffer, 5))
+               if (len >= 11 && !memcmp("refs/heads/", buffer, 11))
                        return 0;
                return -1;
        }
@@ -178,7 +178,7 @@ int validate_headref(const char *path)
                len -= 4;
                while (len && isspace(*buf))
                        buf++, len--;
-               if (len >= 5 && !memcmp("refs/", buf, 5))
+               if (len >= 11 && !memcmp("refs/heads/", buf, 11))
                        return 0;
        }
 
@@ -363,58 +363,19 @@ const char *make_relative_path(const char *abs, const char *base)
 }
 
 /*
- * path = absolute path
- * buf = buffer of at least max(2, strlen(path)+1) bytes
- * It is okay if buf == path, but they should not overlap otherwise.
+ * It is okay if dst == src, but they should not overlap otherwise.
  *
- * Performs the following normalizations on path, storing the result in buf:
- * - Removes trailing slashes.
- * - Removes empty components.
+ * Performs the following normalizations on src, storing the result in dst:
+ * - Ensures that components are separated by '/' (Windows only)
+ * - Squashes sequences of '/'.
  * - Removes "." components.
  * - Removes ".." components, and the components the precede them.
- * "" and paths that contain only slashes are normalized to "/".
- * Returns the length of the output.
+ * Returns failure (non-zero) if a ".." component appears as first path
+ * component anytime during the normalization. Otherwise, returns success (0).
  *
  * Note that this function is purely textual.  It does not follow symlinks,
  * verify the existence of the path, or make any system calls.
  */
-int normalize_absolute_path(char *buf, const char *path)
-{
-       const char *comp_start = path, *comp_end = path;
-       char *dst = buf;
-       int comp_len;
-       assert(buf);
-       assert(path);
-
-       while (*comp_start) {
-               assert(*comp_start == '/');
-               while (*++comp_end && *comp_end != '/')
-                       ; /* nothing */
-               comp_len = comp_end - comp_start;
-
-               if (!strncmp("/",  comp_start, comp_len) ||
-                   !strncmp("/.", comp_start, comp_len))
-                       goto next;
-
-               if (!strncmp("/..", comp_start, comp_len)) {
-                       while (dst > buf && *--dst != '/')
-                               ; /* nothing */
-                       goto next;
-               }
-
-               memmove(dst, comp_start, comp_len);
-               dst += comp_len;
-       next:
-               comp_start = comp_end;
-       }
-
-       if (dst == buf)
-               *dst++ = '/';
-
-       *dst = '\0';
-       return dst - buf;
-}
-
 int normalize_path_copy(char *dst, const char *src)
 {
        char *dst0;
@@ -484,18 +445,12 @@ int normalize_path_copy(char *dst, const char *src)
                 * dst0..dst is prefix portion, and dst[-1] is '/';
                 * go up one level.
                 */
-               dst -= 2; /* go past trailing '/' if any */
-               if (dst < dst0)
+               dst--;  /* go to trailing '/' */
+               if (dst <= dst0)
                        return -1;
-               while (1) {
-                       if (dst <= dst0)
-                               break;
-                       c = *dst--;
-                       if (c == '/') { /* MinGW: cannot be '\\' anymore */
-                               dst += 2;
-                               break;
-                       }
-               }
+               /* Windows: dst[-1] cannot be backslash anymore */
+               while (dst0 < dst && dst[-1] != '/')
+                       dst--;
        }
        *dst = '\0';
        return 0;
@@ -524,15 +479,16 @@ int longest_ancestor_length(const char *path, const char *prefix_list)
                return -1;
 
        for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
-               for (colon = ceil; *colon && *colon != ':'; colon++);
+               for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
                len = colon - ceil;
                if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
                        continue;
                strlcpy(buf, ceil, len+1);
-               len = normalize_absolute_path(buf, buf);
-               /* Strip "trailing slashes" from "/". */
-               if (len == 1)
-                       len = 0;
+               if (normalize_path_copy(buf, buf) < 0)
+                       continue;
+               len = strlen(buf);
+               if (len > 0 && buf[len-1] == '/')
+                       buf[--len] = '\0';
 
                if (!strncmp(path, buf, len) &&
                    path[len] == '/' &&