Merge branch 'js/dirname-basename' into maint
dirname() emulation has been added, as Msys2 lacks it. * js/dirname-basename: mingw: avoid linking to the C library's isalpha() t0060: loosen overly strict expectations t0060: verify that basename() and dirname() work as expected compat/basename.c: provide a dirname() compatibility function compat/basename: make basename() conform to POSIX Refactor skipping DOS drive prefixesmaint
						commit
						07be1da216
					
				|  | @ -1,15 +1,71 @@ | ||||||
| #include "../git-compat-util.h" | #include "../git-compat-util.h" | ||||||
|  | #include "../strbuf.h" | ||||||
|  |  | ||||||
| /* Adapted from libiberty's basename.c.  */ | /* Adapted from libiberty's basename.c.  */ | ||||||
| char *gitbasename (char *path) | char *gitbasename (char *path) | ||||||
| { | { | ||||||
| 	const char *base; | 	const char *base; | ||||||
| 	/* Skip over the disk name in MSDOS pathnames. */ |  | ||||||
| 	if (has_dos_drive_prefix(path)) | 	if (path) | ||||||
| 		path += 2; | 		skip_dos_drive_prefix(&path); | ||||||
|  |  | ||||||
|  | 	if (!path || !*path) | ||||||
|  | 		return "."; | ||||||
|  |  | ||||||
| 	for (base = path; *path; path++) { | 	for (base = path; *path; path++) { | ||||||
| 		if (is_dir_sep(*path)) | 		if (!is_dir_sep(*path)) | ||||||
| 			base = path + 1; | 			continue; | ||||||
|  | 		do { | ||||||
|  | 			path++; | ||||||
|  | 		} while (is_dir_sep(*path)); | ||||||
|  | 		if (*path) | ||||||
|  | 			base = path; | ||||||
|  | 		else | ||||||
|  | 			while (--path != base && is_dir_sep(*path)) | ||||||
|  | 				*path = '\0'; | ||||||
| 	} | 	} | ||||||
| 	return (char *)base; | 	return (char *)base; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | char *gitdirname(char *path) | ||||||
|  | { | ||||||
|  | 	static struct strbuf buf = STRBUF_INIT; | ||||||
|  | 	char *p = path, *slash = NULL, c; | ||||||
|  | 	int dos_drive_prefix; | ||||||
|  |  | ||||||
|  | 	if (!p) | ||||||
|  | 		return "."; | ||||||
|  |  | ||||||
|  | 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p) | ||||||
|  | 		goto dot; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * POSIX.1-2001 says dirname("/") should return "/", and dirname("//") | ||||||
|  | 	 * should return "//", but dirname("///") should return "/" again. | ||||||
|  | 	 */ | ||||||
|  | 	if (is_dir_sep(*p)) { | ||||||
|  | 		if (!p[1] || (is_dir_sep(p[1]) && !p[2])) | ||||||
|  | 			return path; | ||||||
|  | 		slash = ++p; | ||||||
|  | 	} | ||||||
|  | 	while ((c = *(p++))) | ||||||
|  | 		if (is_dir_sep(c)) { | ||||||
|  | 			char *tentative = p - 1; | ||||||
|  |  | ||||||
|  | 			/* POSIX.1-2001 says to ignore trailing slashes */ | ||||||
|  | 			while (is_dir_sep(*p)) | ||||||
|  | 				p++; | ||||||
|  | 			if (*p) | ||||||
|  | 				slash = tentative; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	if (slash) { | ||||||
|  | 		*slash = '\0'; | ||||||
|  | 		return path; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | dot: | ||||||
|  | 	strbuf_reset(&buf); | ||||||
|  | 	strbuf_addf(&buf, "%.*s.", dos_drive_prefix, path); | ||||||
|  | 	return buf.buf; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1932,28 +1932,31 @@ pid_t waitpid(pid_t pid, int *status, int options) | ||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int mingw_skip_dos_drive_prefix(char **path) | ||||||
|  | { | ||||||
|  | 	int ret = has_dos_drive_prefix(*path); | ||||||
|  | 	*path += ret; | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| int mingw_offset_1st_component(const char *path) | int mingw_offset_1st_component(const char *path) | ||||||
| { | { | ||||||
| 	int offset = 0; | 	char *pos = (char *)path; | ||||||
| 	if (has_dos_drive_prefix(path)) |  | ||||||
| 		offset = 2; |  | ||||||
|  |  | ||||||
| 	/* unc paths */ | 	/* unc paths */ | ||||||
| 	else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) { | 	if (!skip_dos_drive_prefix(&pos) && | ||||||
|  | 			is_dir_sep(pos[0]) && is_dir_sep(pos[1])) { | ||||||
| 		/* skip server name */ | 		/* skip server name */ | ||||||
| 		char *pos = strpbrk(path + 2, "\\/"); | 		pos = strpbrk(pos + 2, "\\/"); | ||||||
| 		if (!pos) | 		if (!pos) | ||||||
| 			return 0; /* Error: malformed unc path */ | 			return 0; /* Error: malformed unc path */ | ||||||
|  |  | ||||||
| 		do { | 		do { | ||||||
| 			pos++; | 			pos++; | ||||||
| 		} while (*pos && !is_dir_sep(*pos)); | 		} while (*pos && !is_dir_sep(*pos)); | ||||||
|  |  | ||||||
| 		offset = pos - path; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return offset + is_dir_sep(path[offset]); | 	return pos + is_dir_sep(*pos) - path; | ||||||
| } | } | ||||||
|  |  | ||||||
| int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen) | int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen) | ||||||
|  |  | ||||||
|  | @ -361,7 +361,10 @@ HANDLE winansi_get_osfhandle(int fd); | ||||||
|  * git specific compatibility |  * git specific compatibility | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') | #define has_dos_drive_prefix(path) \ | ||||||
|  | 	(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0) | ||||||
|  | int mingw_skip_dos_drive_prefix(char **path); | ||||||
|  | #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix | ||||||
| #define is_dir_sep(c) ((c) == '/' || (c) == '\\') | #define is_dir_sep(c) ((c) == '/' || (c) == '\\') | ||||||
| static inline char *mingw_find_last_dir_sep(const char *path) | static inline char *mingw_find_last_dir_sep(const char *path) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -253,6 +253,8 @@ struct itimerval { | ||||||
| #else | #else | ||||||
| #define basename gitbasename | #define basename gitbasename | ||||||
| extern char *gitbasename(char *); | extern char *gitbasename(char *); | ||||||
|  | #define dirname gitdirname | ||||||
|  | extern char *gitdirname(char *); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef NO_ICONV | #ifndef NO_ICONV | ||||||
|  | @ -335,6 +337,14 @@ static inline int git_has_dos_drive_prefix(const char *path) | ||||||
| #define has_dos_drive_prefix git_has_dos_drive_prefix | #define has_dos_drive_prefix git_has_dos_drive_prefix | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef skip_dos_drive_prefix | ||||||
|  | static inline int git_skip_dos_drive_prefix(char **path) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #define skip_dos_drive_prefix git_skip_dos_drive_prefix | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifndef is_dir_sep | #ifndef is_dir_sep | ||||||
| static inline int git_is_dir_sep(int c) | static inline int git_is_dir_sep(int c) | ||||||
| { | { | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								path.c
								
								
								
								
							
							
						
						
									
										14
									
								
								path.c
								
								
								
								
							|  | @ -782,13 +782,10 @@ const char *relative_path(const char *in, const char *prefix, | ||||||
| 	else if (!prefix_len) | 	else if (!prefix_len) | ||||||
| 		return in; | 		return in; | ||||||
|  |  | ||||||
| 	if (have_same_root(in, prefix)) { | 	if (have_same_root(in, prefix)) | ||||||
| 		/* bypass dos_drive, for "c:" is identical to "C:" */ | 		/* bypass dos_drive, for "c:" is identical to "C:" */ | ||||||
| 		if (has_dos_drive_prefix(in)) { | 		i = j = has_dos_drive_prefix(in); | ||||||
| 			i = 2; | 	else { | ||||||
| 			j = 2; |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		return in; | 		return in; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -943,11 +940,10 @@ const char *remove_leading_path(const char *in, const char *prefix) | ||||||
| int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) | int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) | ||||||
| { | { | ||||||
| 	char *dst0; | 	char *dst0; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
| 	if (has_dos_drive_prefix(src)) { | 	for (i = has_dos_drive_prefix(src); i > 0; i--) | ||||||
| 		*dst++ = *src++; | 		*dst++ = *src++; | ||||||
| 		*dst++ = *src++; |  | ||||||
| 	} |  | ||||||
| 	dst0 = dst; | 	dst0 = dst; | ||||||
|  |  | ||||||
| 	if (is_dir_sep(*src)) { | 	if (is_dir_sep(*src)) { | ||||||
|  |  | ||||||
|  | @ -59,6 +59,9 @@ case $(uname -s) in | ||||||
| 	;; | 	;; | ||||||
| esac | esac | ||||||
|  |  | ||||||
|  | test_expect_success basename 'test-path-utils basename' | ||||||
|  | test_expect_success dirname 'test-path-utils dirname' | ||||||
|  |  | ||||||
| norm_path "" "" | norm_path "" "" | ||||||
| norm_path . "" | norm_path . "" | ||||||
| norm_path ./ "" | norm_path ./ "" | ||||||
|  |  | ||||||
|  | @ -39,6 +39,130 @@ static void normalize_argv_string(const char **var, const char *input) | ||||||
| 		die("Bad value: %s\n", input); | 		die("Bad value: %s\n", input); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct test_data { | ||||||
|  | 	const char *from;  /* input:  transform from this ... */ | ||||||
|  | 	const char *to;    /* output: ... to this.            */ | ||||||
|  | 	const char *alternative; /* output: ... or this.      */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int test_function(struct test_data *data, char *(*func)(char *input), | ||||||
|  | 	const char *funcname) | ||||||
|  | { | ||||||
|  | 	int failed = 0, i; | ||||||
|  | 	char buffer[1024]; | ||||||
|  | 	char *to; | ||||||
|  |  | ||||||
|  | 	for (i = 0; data[i].to; i++) { | ||||||
|  | 		if (!data[i].from) | ||||||
|  | 			to = func(NULL); | ||||||
|  | 		else { | ||||||
|  | 			strcpy(buffer, data[i].from); | ||||||
|  | 			to = func(buffer); | ||||||
|  | 		} | ||||||
|  | 		if (!strcmp(to, data[i].to)) | ||||||
|  | 			continue; | ||||||
|  | 		if (!data[i].alternative) | ||||||
|  | 			error("FAIL: %s(%s) => '%s' != '%s'\n", | ||||||
|  | 				funcname, data[i].from, to, data[i].to); | ||||||
|  | 		else if (!strcmp(to, data[i].alternative)) | ||||||
|  | 			continue; | ||||||
|  | 		else | ||||||
|  | 			error("FAIL: %s(%s) => '%s' != '%s', '%s'\n", | ||||||
|  | 				funcname, data[i].from, to, data[i].to, | ||||||
|  | 				data[i].alternative); | ||||||
|  | 		failed = 1; | ||||||
|  | 	} | ||||||
|  | 	return failed; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct test_data basename_data[] = { | ||||||
|  | 	/* --- POSIX type paths --- */ | ||||||
|  | 	{ NULL,              "."    }, | ||||||
|  | 	{ "",                "."    }, | ||||||
|  | 	{ ".",               "."    }, | ||||||
|  | 	{ "..",              ".."   }, | ||||||
|  | 	{ "/",               "/"    }, | ||||||
|  | 	{ "//",              "/", "//" }, | ||||||
|  | 	{ "///",             "/", "//" }, | ||||||
|  | 	{ "////",            "/", "//" }, | ||||||
|  | 	{ "usr",             "usr"  }, | ||||||
|  | 	{ "/usr",            "usr"  }, | ||||||
|  | 	{ "/usr/",           "usr"  }, | ||||||
|  | 	{ "/usr//",          "usr"  }, | ||||||
|  | 	{ "/usr/lib",        "lib"  }, | ||||||
|  | 	{ "usr/lib",         "lib"  }, | ||||||
|  | 	{ "usr/lib///",      "lib"  }, | ||||||
|  |  | ||||||
|  | #if defined(__MINGW32__) || defined(_MSC_VER) | ||||||
|  | 	/* --- win32 type paths --- */ | ||||||
|  | 	{ "\\usr",           "usr"  }, | ||||||
|  | 	{ "\\usr\\",         "usr"  }, | ||||||
|  | 	{ "\\usr\\\\",       "usr"  }, | ||||||
|  | 	{ "\\usr\\lib",      "lib"  }, | ||||||
|  | 	{ "usr\\lib",        "lib"  }, | ||||||
|  | 	{ "usr\\lib\\\\\\",  "lib"  }, | ||||||
|  | 	{ "C:/usr",          "usr"  }, | ||||||
|  | 	{ "C:/usr",          "usr"  }, | ||||||
|  | 	{ "C:/usr/",         "usr"  }, | ||||||
|  | 	{ "C:/usr//",        "usr"  }, | ||||||
|  | 	{ "C:/usr/lib",      "lib"  }, | ||||||
|  | 	{ "C:usr/lib",       "lib"  }, | ||||||
|  | 	{ "C:usr/lib///",    "lib"  }, | ||||||
|  | 	{ "C:",              "."    }, | ||||||
|  | 	{ "C:a",             "a"    }, | ||||||
|  | 	{ "C:/",             "/"    }, | ||||||
|  | 	{ "C:///",           "/"    }, | ||||||
|  | 	{ "\\",              "\\", "/" }, | ||||||
|  | 	{ "\\\\",            "\\", "/" }, | ||||||
|  | 	{ "\\\\\\",          "\\", "/" }, | ||||||
|  | #endif | ||||||
|  | 	{ NULL,              NULL   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct test_data dirname_data[] = { | ||||||
|  | 	/* --- POSIX type paths --- */ | ||||||
|  | 	{ NULL,              "."      }, | ||||||
|  | 	{ "",                "."      }, | ||||||
|  | 	{ ".",               "."      }, | ||||||
|  | 	{ "..",              "."      }, | ||||||
|  | 	{ "/",               "/"      }, | ||||||
|  | 	{ "//",              "/", "//" }, | ||||||
|  | 	{ "///",             "/", "//" }, | ||||||
|  | 	{ "////",            "/", "//" }, | ||||||
|  | 	{ "usr",             "."      }, | ||||||
|  | 	{ "/usr",            "/"      }, | ||||||
|  | 	{ "/usr/",           "/"      }, | ||||||
|  | 	{ "/usr//",          "/"      }, | ||||||
|  | 	{ "/usr/lib",        "/usr"   }, | ||||||
|  | 	{ "usr/lib",         "usr"    }, | ||||||
|  | 	{ "usr/lib///",      "usr"    }, | ||||||
|  |  | ||||||
|  | #if defined(__MINGW32__) || defined(_MSC_VER) | ||||||
|  | 	/* --- win32 type paths --- */ | ||||||
|  | 	{ "\\",              "\\"     }, | ||||||
|  | 	{ "\\\\",            "\\\\"   }, | ||||||
|  | 	{ "\\usr",           "\\"     }, | ||||||
|  | 	{ "\\usr\\",         "\\"     }, | ||||||
|  | 	{ "\\usr\\\\",       "\\"     }, | ||||||
|  | 	{ "\\usr\\lib",      "\\usr"  }, | ||||||
|  | 	{ "usr\\lib",        "usr"    }, | ||||||
|  | 	{ "usr\\lib\\\\\\",  "usr"    }, | ||||||
|  | 	{ "C:a",             "C:."    }, | ||||||
|  | 	{ "C:/",             "C:/"    }, | ||||||
|  | 	{ "C:///",           "C:/"    }, | ||||||
|  | 	{ "C:/usr",          "C:/"    }, | ||||||
|  | 	{ "C:/usr/",         "C:/"    }, | ||||||
|  | 	{ "C:/usr//",        "C:/"    }, | ||||||
|  | 	{ "C:/usr/lib",      "C:/usr" }, | ||||||
|  | 	{ "C:usr/lib",       "C:usr"  }, | ||||||
|  | 	{ "C:usr/lib///",    "C:usr"  }, | ||||||
|  | 	{ "\\\\\\",          "\\"     }, | ||||||
|  | 	{ "\\\\\\\\",        "\\"     }, | ||||||
|  | 	{ "C:",              "C:.", "." }, | ||||||
|  | #endif | ||||||
|  | 	{ NULL,              NULL     } | ||||||
|  | }; | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
| 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { | 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { | ||||||
|  | @ -133,6 +257,12 @@ int main(int argc, char **argv) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (argc == 2 && !strcmp(argv[1], "basename")) | ||||||
|  | 		return test_function(basename_data, basename, argv[1]); | ||||||
|  |  | ||||||
|  | 	if (argc == 2 && !strcmp(argv[1], "dirname")) | ||||||
|  | 		return test_function(dirname_data, dirname, argv[1]); | ||||||
|  |  | ||||||
| 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0], | 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0], | ||||||
| 		argv[1] ? argv[1] : "(there was none)"); | 		argv[1] ? argv[1] : "(there was none)"); | ||||||
| 	return 1; | 	return 1; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano