return path;
}
+char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+{
+ va_list args;
+ unsigned len;
+
+ va_start(args, fmt);
+ len = vsnprintf(buf, n, fmt, args);
+ va_end(args);
+ if (len >= n) {
+ strlcpy(buf, bad_path, n);
+ return buf;
+ }
+ return cleanup_path(buf);
+}
+
+static char *git_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+{
+ const char *git_dir = get_git_dir();
+ size_t len;
+
+ len = strlen(git_dir);
+ if (n < len + 1)
+ goto bad;
+ memcpy(buf, git_dir, len);
+ if (len && !is_dir_sep(git_dir[len-1]))
+ buf[len++] = '/';
+ len += vsnprintf(buf + len, n - len, fmt, args);
+ if (len >= n)
+ goto bad;
+ return cleanup_path(buf);
+bad:
+ strlcpy(buf, bad_path, n);
+ return buf;
+}
+
+char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ (void)git_vsnpath(buf, n, fmt, args);
+ va_end(args);
+ return buf;
+}
+
+char *git_pathdup(const char *fmt, ...)
+{
+ char path[PATH_MAX];
+ va_list args;
+ va_start(args, fmt);
+ (void)git_vsnpath(path, sizeof(path), fmt, args);
+ va_end(args);
+ return xstrdup(path);
+}
+
char *mkpath(const char *fmt, ...)
{
va_list args;
/* git_mkstemp() - create tmp file honoring TMPDIR variable */
int git_mkstemp(char *path, size_t len, const char *template)
{
- char *env, *pch = path;
-
- if ((env = getenv("TMPDIR")) == NULL) {
- strcpy(pch, "/tmp/");
- len -= 5;
- pch += 5;
- } else {
- size_t n = snprintf(pch, len, "%s/", env);
-
- len -= n;
- pch += n;
+ const char *tmp;
+ size_t n;
+
+ tmp = getenv("TMPDIR");
+ if (!tmp)
+ tmp = "/tmp";
+ n = snprintf(path, len, "%s/%s", tmp, template);
+ if (len <= n) {
+ errno = ENAMETOOLONG;
+ return -1;
}
-
- strlcpy(pch, template, len);
-
return mkstemp(path);
}
struct stat st;
char *buf, buffer[256];
unsigned char sha1[20];
- int len, fd;
+ int fd;
+ ssize_t len;
if (lstat(path, &st) < 0)
return -1;
if (lstat(path, &st) < 0)
return -1;
mode = st.st_mode;
- if (mode & S_IRUSR)
- mode |= (shared_repository == PERM_GROUP
- ? S_IRGRP
- : (shared_repository == PERM_EVERYBODY
- ? (S_IRGRP|S_IROTH)
- : 0));
-
- if (mode & S_IWUSR)
- mode |= S_IWGRP;
-
- if (mode & S_IXUSR)
- mode |= (shared_repository == PERM_GROUP
- ? S_IXGRP
- : (shared_repository == PERM_EVERYBODY
- ? (S_IXGRP|S_IXOTH)
- : 0));
- if (S_ISDIR(mode))
- mode |= S_ISGID;
+
+ if (shared_repository) {
+ int tweak = shared_repository;
+ if (!(mode & S_IWUSR))
+ tweak &= ~0222;
+ mode |= tweak;
+ } else {
+ /* Preserve old PERM_UMASK behaviour */
+ if (mode & S_IWUSR)
+ mode |= S_IWGRP;
+ }
+
+ if (S_ISDIR(mode)) {
+ mode |= FORCE_DIR_SET_GID;
+
+ /* Copy read bits to execute bits */
+ mode |= (shared_repository & 0444) >> 2;
+ }
+
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
return -2;
return 0;
}
+
+const char *make_relative_path(const char *abs, const char *base)
+{
+ static char buf[PATH_MAX + 1];
+ int baselen;
+ if (!base)
+ return abs;
+ baselen = strlen(base);
+ if (prefixcmp(abs, base))
+ return abs;
+ if (abs[baselen] == '/')
+ baselen++;
+ else if (base[baselen - 1] != '/')
+ return abs;
+ strcpy(buf, abs + baselen);
+ return buf;
+}
+
+/*
+ * 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.
+ *
+ * Performs the following normalizations on path, storing the result in buf:
+ * - Removes trailing slashes.
+ * - Removes empty components.
+ * - 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.
+ *
+ * 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;
+}
+
+/*
+ * path = Canonical absolute path
+ * prefix_list = Colon-separated list of absolute paths
+ *
+ * Determines, for each path in prefix_list, whether the "prefix" really
+ * is an ancestor directory of path. Returns the length of the longest
+ * ancestor directory, excluding any trailing slashes, or -1 if no prefix
+ * is an ancestor. (Note that this means 0 is returned if prefix_list is
+ * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
+ * are not considered to be their own ancestors. path must be in a
+ * canonical form: empty components, or "." or ".." components are not
+ * allowed. prefix_list may be null, which is like "".
+ */
+int longest_ancestor_length(const char *path, const char *prefix_list)
+{
+ char buf[PATH_MAX+1];
+ const char *ceil, *colon;
+ int len, max_len = -1;
+
+ if (prefix_list == NULL || !strcmp(path, "/"))
+ return -1;
+
+ for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
+ for (colon = ceil; *colon && *colon != ':'; 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 (!strncmp(path, buf, len) &&
+ path[len] == '/' &&
+ len > max_len) {
+ max_len = len;
+ }
+ }
+
+ return max_len;
+}