Re: [PATCH v4 3/4] Provide a dirname() function when NO_LIBGEN_H=YesPlease
From: Ramsay Jones <hidden>
Date: 2016-06-15 23:07:43
On 12/01/16 07:57, Johannes Schindelin wrote:
quoted hunk ↗ jump to hunk
When there is no `libgen.h` to our disposal, we miss the `dirname()` function. So far, we only had one user of that function: credential-cache--daemon (which was only compiled when Unix sockets are available, anyway). But now we also have `builtin/am.c` as user, so we need it. Since `dirname()` is a sibling of `basename()`, we simply put our very own `gitdirname()` implementation next to `gitbasename()` and use it if `NO_LIBGEN_H` has been set. Signed-off-by: Johannes Schindelin <redacted> --- compat/basename.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ git-compat-util.h | 2 ++ 2 files changed, 46 insertions(+)diff --git a/compat/basename.c b/compat/basename.c index 0f1b0b0..96bd953 100644 --- a/compat/basename.c +++ b/compat/basename.c@@ -1,4 +1,5 @@ #include "../git-compat-util.h" +#include "../strbuf.h" /* Adapted from libiberty's basename.c. */ char *gitbasename (char *path)@@ -25,3 +26,46 @@ char *gitbasename (char *path) } 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; +}
Again, I find my version much easier to read:
char *gitdirname(char *path)
{
char *p, *start;
if (!path || !*path)
return ".";
start = path;
/* skip drive designator, if any */
if (has_dos_drive_prefix(path))
start += 2;
/* check for // */
if (strcmp(start, "//") == 0)
return path;
/* check for \\ */
if (is_dir_sep('\\') && strcmp(start, "\\\\") == 0)
return path;
/* trim trailing directory separators */
p = path + strlen(path) - 1;
while (is_dir_sep(*p)) {
if (p == start)
return path;
*p-- = '\0';
}
/* find begining of last path component */
while (p > start && !is_dir_sep(*p))
p--;
/* terminate dirname */
if (p == start && !is_dir_sep(*p))
*p++ = '.';
else if (p == start)
p++;
*p = '\0';
return path;
}
quoted hunk ↗ jump to hunk
diff --git a/git-compat-util.h b/git-compat-util.h index fbb11bb..5f72f1c 100644 --- a/git-compat-util.h +++ b/git-compat-util.h@@ -253,6 +253,8 @@ struct itimerval { #else
Also, when compiling on Cygwin with NO_LIBGEN_H, I need to include the following here: #undef basename in order to suppress approx 230 warnings about the redefinition of the basename macro. (I suppose that should go in the previous commit. dunno)
#define basename gitbasename extern char *gitbasename(char *); +#define dirname gitdirname +extern char *gitdirname(char *); #endif #ifndef NO_ICONV
ATB, Ramsay Jones