Merge branch 'js/maint-1.6.0-path-normalize'

* js/maint-1.6.0-path-normalize:
  Remove unused normalize_absolute_path()
  Test and fix normalize_path_copy()
  Fix GIT_CEILING_DIRECTORIES on Windows
  Move sanitary_path_copy() to path.c and rename it to normalize_path_copy()
  Make test-path-utils more robust against incorrect use
This commit is contained in:
Junio C Hamano 2009-02-10 21:30:52 -08:00
commit 6e5d7ddc49
6 changed files with 115 additions and 152 deletions

View file

@ -627,7 +627,7 @@ int is_directory(const char *);
const char *make_absolute_path(const char *path); const char *make_absolute_path(const char *path);
const char *make_nonrelative_path(const char *path); const char *make_nonrelative_path(const char *path);
const char *make_relative_path(const char *abs, const char *base); const char *make_relative_path(const char *abs, const char *base);
int normalize_absolute_path(char *buf, const char *path); int normalize_path_copy(char *dst, const char *src);
int longest_ancestor_length(const char *path, const char *prefix_list); int longest_ancestor_length(const char *path, const char *prefix_list);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */ /* Read and unpack a sha1 file into memory, write memory to a sha1 file */

124
path.c
View file

@ -363,56 +363,97 @@ const char *make_relative_path(const char *abs, const char *base)
} }
/* /*
* path = absolute path * It is okay if dst == src, but they should not overlap otherwise.
* 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: * Performs the following normalizations on src, storing the result in dst:
* - Removes trailing slashes. * - Ensures that components are separated by '/' (Windows only)
* - Removes empty components. * - Squashes sequences of '/'.
* - Removes "." components. * - Removes "." components.
* - Removes ".." components, and the components the precede them. * - Removes ".." components, and the components the precede them.
* "" and paths that contain only slashes are normalized to "/". * Returns failure (non-zero) if a ".." component appears as first path
* Returns the length of the output. * component anytime during the normalization. Otherwise, returns success (0).
* *
* Note that this function is purely textual. It does not follow symlinks, * Note that this function is purely textual. It does not follow symlinks,
* verify the existence of the path, or make any system calls. * verify the existence of the path, or make any system calls.
*/ */
int normalize_absolute_path(char *buf, const char *path) int normalize_path_copy(char *dst, const char *src)
{ {
const char *comp_start = path, *comp_end = path; char *dst0;
char *dst = buf;
int comp_len;
assert(buf);
assert(path);
while (*comp_start) { if (has_dos_drive_prefix(src)) {
assert(*comp_start == '/'); *dst++ = *src++;
while (*++comp_end && *comp_end != '/') *dst++ = *src++;
; /* nothing */ }
comp_len = comp_end - comp_start; dst0 = dst;
if (!strncmp("/", comp_start, comp_len) || if (is_dir_sep(*src)) {
!strncmp("/.", comp_start, comp_len)) *dst++ = '/';
goto next; while (is_dir_sep(*src))
src++;
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) for (;;) {
*dst++ = '/'; 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--; /* go to trailing '/' */
if (dst <= dst0)
return -1;
/* Windows: dst[-1] cannot be backslash anymore */
while (dst0 < dst && dst[-1] != '/')
dst--;
}
*dst = '\0'; *dst = '\0';
return dst - buf; return 0;
} }
/* /*
@ -438,15 +479,16 @@ int longest_ancestor_length(const char *path, const char *prefix_list)
return -1; return -1;
for (colon = ceil = prefix_list; *colon; ceil = colon+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; len = colon - ceil;
if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
continue; continue;
strlcpy(buf, ceil, len+1); strlcpy(buf, ceil, len+1);
len = normalize_absolute_path(buf, buf); if (normalize_path_copy(buf, buf) < 0)
/* Strip "trailing slashes" from "/". */ continue;
if (len == 1) len = strlen(buf);
len = 0; if (len > 0 && buf[len-1] == '/')
buf[--len] = '\0';
if (!strncmp(path, buf, len) && if (!strncmp(path, buf, len) &&
path[len] == '/' && path[len] == '/' &&

88
setup.c
View file

@ -4,92 +4,6 @@
static int inside_git_dir = -1; static int inside_git_dir = -1;
static int inside_work_tree = -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 *prefix_path(const char *prefix, int len, const char *path)
{ {
const char *orig = path; const char *orig = path;
@ -101,7 +15,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
memcpy(sanitized, prefix, len); memcpy(sanitized, prefix, len);
strcpy(sanitized + len, path); strcpy(sanitized + len, path);
} }
if (sanitary_path_copy(sanitized, sanitized)) if (normalize_path_copy(sanitized, sanitized))
goto error_out; goto error_out;
if (is_absolute_path(orig)) { if (is_absolute_path(orig)) {
const char *work_tree = get_git_work_tree(); const char *work_tree = get_git_work_tree();

View file

@ -8,36 +8,37 @@ test_description='Test various path utilities'
. ./test-lib.sh . ./test-lib.sh
norm_abs() { norm_abs() {
test_expect_success "normalize absolute" \ test_expect_success "normalize absolute: $1 => $2" \
"test \$(test-path-utils normalize_absolute_path '$1') = '$2'" "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
} }
ancestor() { ancestor() {
test_expect_success "longest ancestor" \ test_expect_success "longest ancestor: $1 $2 => $3" \
"test \$(test-path-utils longest_ancestor_length '$1' '$2') = '$3'" "test \"\$(test-path-utils longest_ancestor_length '$1' '$2')\" = '$3'"
} }
norm_abs "" / norm_abs "" ""
norm_abs / / norm_abs / /
norm_abs // / norm_abs // /
norm_abs /// / norm_abs /// /
norm_abs /. / norm_abs /. /
norm_abs /./ / norm_abs /./ /
norm_abs /./.. / norm_abs /./.. ++failed++
norm_abs /../. / norm_abs /../. ++failed++
norm_abs /./../.// / norm_abs /./../.// ++failed++
norm_abs /dir/.. / norm_abs /dir/.. /
norm_abs /dir/sub/../.. / norm_abs /dir/sub/../.. /
norm_abs /dir/sub/../../.. ++failed++
norm_abs /dir /dir norm_abs /dir /dir
norm_abs /dir// /dir norm_abs /dir// /dir/
norm_abs /./dir /dir norm_abs /./dir /dir
norm_abs /dir/. /dir norm_abs /dir/. /dir/
norm_abs /dir///./ /dir norm_abs /dir///./ /dir/
norm_abs /dir//sub/.. /dir norm_abs /dir//sub/.. /dir/
norm_abs /dir/sub/../ /dir norm_abs /dir/sub/../ /dir/
norm_abs //dir/sub/../. /dir norm_abs //dir/sub/../. /dir/
norm_abs /dir/s1/../s2/ /dir/s2 norm_abs /dir/s1/../s2/ /dir/s2/
norm_abs /d1/s1///s2/..//../s3/ /d1/s3 norm_abs /d1/s1///s2/..//../s3/ /d1/s3/
norm_abs /d1/s1//../s2/../../d2 /d2 norm_abs /d1/s1//../s2/../../d2 /d2
norm_abs /d1/.../d2 /d1/.../d2 norm_abs /d1/.../d2 /d1/.../d2
norm_abs /d1/..././../d2 /d1/d2 norm_abs /d1/..././../d2 /d1/d2

View file

@ -93,13 +93,13 @@ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
test_prefix subdir_ceil_at_subdi_slash "sub/dir/" test_prefix subdir_ceil_at_subdi_slash "sub/dir/"
GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub" GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub"
test_fail second_of_two test_fail second_of_two
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:bar" GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:/bar"
test_fail first_of_two test_fail first_of_two
GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub:bar" GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub:/bar"
test_fail second_of_three test_fail second_of_three

View file

@ -2,11 +2,13 @@
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
char *buf = xmalloc(PATH_MAX + 1); char *buf = xmalloc(PATH_MAX + 1);
int rv = normalize_absolute_path(buf, argv[2]); int rv = normalize_path_copy(buf, argv[2]);
assert(strlen(buf) == rv); if (rv)
buf = "++failed++";
puts(buf); puts(buf);
return 0;
} }
if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) { if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) {
@ -15,12 +17,16 @@ int main(int argc, char **argv)
argc--; argc--;
argv++; argv++;
} }
return 0;
} }
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
int len = longest_ancestor_length(argv[2], argv[3]); int len = longest_ancestor_length(argv[2], argv[3]);
printf("%d\n", len); printf("%d\n", len);
return 0;
} }
return 0; fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
} }