summaryrefslogtreecommitdiff
path: root/setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c206
1 files changed, 77 insertions, 129 deletions
diff --git a/setup.c b/setup.c
index 6cf909463d..0717a98d16 100644
--- a/setup.c
+++ b/setup.c
@@ -4,92 +4,6 @@
static int inside_git_dir = -1;
static int inside_work_tree = -1;
-static int sanitary_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;
-}
-
const char *prefix_path(const char *prefix, int len, const char *path)
{
const char *orig = path;
@@ -101,18 +15,19 @@ const char *prefix_path(const char *prefix, int len, const char *path)
memcpy(sanitized, prefix, len);
strcpy(sanitized + len, path);
}
- if (sanitary_path_copy(sanitized, sanitized))
+ if (normalize_path_copy(sanitized, sanitized))
goto error_out;
if (is_absolute_path(orig)) {
+ size_t len, total;
const char *work_tree = get_git_work_tree();
- size_t len = strlen(work_tree);
- size_t total = strlen(sanitized) + 1;
+ if (!work_tree)
+ goto error_out;
+ len = strlen(work_tree);
+ total = strlen(sanitized) + 1;
if (strncmp(sanitized, work_tree, len) ||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
error_out:
- error("'%s' is outside repository", orig);
- free(sanitized);
- return NULL;
+ die("'%s' is outside repository", orig);
}
if (sanitized[len] == '/')
len++;
@@ -129,7 +44,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
static char path[PATH_MAX];
-#ifndef __MINGW32__
+#ifndef WIN32
if (!pfx || !*pfx || is_absolute_path(arg))
return arg;
memcpy(path, pfx, pfx_len);
@@ -149,6 +64,31 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
return path;
}
+int check_filename(const char *prefix, const char *arg)
+{
+ const char *name;
+ struct stat st;
+
+ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+ if (!lstat(name, &st))
+ return 1; /* file exists */
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0; /* file does not exist */
+ die_errno("failed to stat '%s'", arg);
+}
+
+static void NORETURN die_verify_filename(const char *prefix, const char *arg)
+{
+ unsigned char sha1[20];
+ unsigned mode;
+ /* try a detailed diagnostic ... */
+ get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
+ /* ... or fall back the most general message. */
+ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+ "Use '--' to separate paths from revisions", arg);
+
+}
+
/*
* Verify a filename that we got as an argument for a pathspec
* entry. Note that a filename that begins with "-" never verifies
@@ -158,18 +98,11 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
*/
void verify_filename(const char *prefix, const char *arg)
{
- const char *name;
- struct stat st;
-
if (*arg == '-')
die("bad flag '%s' used after filename", arg);
- name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
- if (!lstat(name, &st))
+ if (check_filename(prefix, arg))
return;
- if (errno == ENOENT)
- die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
- "Use '--' to separate paths from revisions", arg);
- die("'%s': %s", arg, strerror(errno));
+ die_verify_filename(prefix, arg);
}
/*
@@ -179,19 +112,14 @@ void verify_filename(const char *prefix, const char *arg)
*/
void verify_non_filename(const char *prefix, const char *arg)
{
- const char *name;
- struct stat st;
-
if (!is_inside_work_tree() || is_inside_git_dir())
return;
if (*arg == '-')
return; /* flag */
- name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
- if (!lstat(name, &st))
- die("ambiguous argument '%s': both revision and filename\n"
- "Use '--' to separate filenames from revisions", arg);
- if (errno != ENOENT && errno != ENOTDIR)
- die("'%s': %s", arg, strerror(errno));
+ if (!check_filename(prefix, arg))
+ return;
+ die("ambiguous argument '%s': both revision and filename\n"
+ "Use '--' to separate filenames from revisions", arg);
}
const char **get_pathspec(const char *prefix, const char **pathspec)
@@ -216,10 +144,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
prefixlen = prefix ? strlen(prefix) : 0;
while (*src) {
const char *p = prefix_path(prefix, prefixlen, *src);
- if (p)
- *(dst++) = p;
- else
- exit(128); /* error message already given */
+ *(dst++) = p;
src++;
}
*dst = NULL;
@@ -281,7 +206,7 @@ int is_inside_work_tree(void)
}
/*
- * set_work_tree() is only ever called if you set GIT_DIR explicitely.
+ * set_work_tree() is only ever called if you set GIT_DIR explicitly.
* The old behaviour (which we retain here) is to set the work tree root
* to the cwd, unless overridden by the config, the command line, or
* GIT_WORK_TREE.
@@ -338,6 +263,8 @@ static int check_repository_format_gently(int *nongit_ok)
const char *read_gitfile_gently(const char *path)
{
char *buf;
+ char *dir;
+ const char *slash;
struct stat st;
int fd;
size_t len;
@@ -348,7 +275,7 @@ const char *read_gitfile_gently(const char *path)
return NULL;
fd = open(path, O_RDONLY);
if (fd < 0)
- die("Error opening %s: %s", path, strerror(errno));
+ die_errno("Error opening '%s'", path);
buf = xmalloc(st.st_size + 1);
len = read_in_full(fd, buf, st.st_size);
close(fd);
@@ -362,9 +289,23 @@ const char *read_gitfile_gently(const char *path)
if (len < 9)
die("No path in gitfile: %s", path);
buf[len] = '\0';
- if (!is_git_directory(buf + 8))
- die("Not a git repository: %s", buf + 8);
- path = make_absolute_path(buf + 8);
+ dir = buf + 8;
+
+ if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
+ size_t pathlen = slash+1 - path;
+ size_t dirlen = pathlen + len - 8;
+ dir = xmalloc(dirlen + 1);
+ strncpy(dir, path, pathlen);
+ strncpy(dir + pathlen, buf + 8, len - 8);
+ dir[dirlen] = '\0';
+ free(buf);
+ buf = dir;
+ }
+
+ if (!is_git_directory(dir))
+ die("Not a git repository: %s", dir);
+ path = make_absolute_path(dir);
+
free(buf);
return path;
}
@@ -418,7 +359,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
return NULL;
set_git_dir(make_absolute_path(gitdirenv));
if (chdir(work_tree_env) < 0)
- die ("Could not chdir to %s", work_tree_env);
+ die_errno ("Could not chdir to '%s'", work_tree_env);
strcat(buffer, "/");
return retval;
}
@@ -430,7 +371,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
}
if (!getcwd(cwd, sizeof(cwd)-1))
- die("Unable to read current working directory");
+ die_errno("Unable to read current working directory");
ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -461,7 +402,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
inside_git_dir = 1;
if (!work_tree_env)
inside_work_tree = 0;
- setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+ if (offset != len) {
+ cwd[offset] = '\0';
+ set_git_dir(cwd);
+ } else
+ set_git_dir(".");
check_repository_format_gently(nongit_ok);
return NULL;
}
@@ -469,13 +414,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
if (offset <= ceil_offset) {
if (nongit_ok) {
if (chdir(cwd))
- die("Cannot come back to cwd");
+ die_errno("Cannot come back to cwd");
*nongit_ok = 1;
return NULL;
}
- die("Not a git repository");
+ die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
}
- chdir("..");
+ if (chdir(".."))
+ die_errno("Cannot change to '%s/..'", cwd);
}
inside_git_dir = 0;
@@ -520,7 +466,7 @@ int git_config_perm(const char *var, const char *value)
/*
* Treat values 0, 1 and 2 as compatibility cases, otherwise it is
- * a chmod value.
+ * a chmod value to restrict to.
*/
switch (i) {
case PERM_UMASK: /* 0 */
@@ -542,7 +488,7 @@ int git_config_perm(const char *var, const char *value)
* Mask filemode value. Others can not get write permission.
* x flags for directories are handled separately.
*/
- return i & 0666;
+ return -(i & 0666);
}
int check_repository_format_version(const char *var, const char *value, void *cb)
@@ -579,8 +525,10 @@ const char *setup_git_directory(void)
static char buffer[PATH_MAX + 1];
char *rel;
if (retval && chdir(retval))
- die ("Could not jump back into original cwd");
+ die_errno ("Could not jump back into original cwd");
rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
+ if (rel && *rel && chdir(get_git_work_tree()))
+ die_errno ("Could not jump to working directory");
return rel && *rel ? strcat(rel, "/") : NULL;
}