From fd85bff53ed2f054b26750f44defd9203967b136 Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Sun, 18 Sep 2016 20:47:55 +0000 Subject: [PATCH] Replace dirname(3) by a copy that complies to POSIX. It turns out that the path normalization that our brand new copy of dirname(3) does is actually not allowed by the draft version of the upcoming version of POSIX. It has to behave identically to the dirname(1) utility. This change replaces our new dirname(3) implementation by yet another version that doesn't implement the path normalization logic; it merely looks for the end of the directory name and overwrites that with a null byte. More details: See note #3370 at http://austingroupbugs.net/view.php?id=1073 PR: 212193 Reviewed by: emaste, jilles Differential Revision: https://reviews.freebsd.org/D7790 --- lib/libc/gen/dirname.3 | 7 +---- lib/libc/gen/dirname.c | 65 ++++++++++++++++-------------------------- 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/lib/libc/gen/dirname.3 b/lib/libc/gen/dirname.3 index 18405f9af6ef..60f44e298614 100644 --- a/lib/libc/gen/dirname.3 +++ b/lib/libc/gen/dirname.3 @@ -16,7 +16,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 12, 2016 +.Dd September 5, 2016 .Dt DIRNAME 3 .Os .Sh NAME @@ -60,11 +60,6 @@ space instead. The advantage of the former approach is that it ensures thread-safety, while also placing no upper limit on the supported length of the pathname. -.Pp -The algorithm used by this implementation also discards redundant -slashes and -.Qq \&. -pathname components from the pathname string. .Sh SEE ALSO .Xr basename 1 , .Xr dirname 1 , diff --git a/lib/libc/gen/dirname.c b/lib/libc/gen/dirname.c index 621122b90c40..ae7b928d54eb 100644 --- a/lib/libc/gen/dirname.c +++ b/lib/libc/gen/dirname.c @@ -27,64 +27,47 @@ __FBSDID("$FreeBSD$"); #include -#include #include char * (dirname)(char *path) { - const char *in, *prev, *begin, *end; - char *out; - size_t prevlen; - bool skipslash; + char *end; /* * If path is a null pointer or points to an empty string, * dirname() shall return a pointer to the string ".". */ if (path == NULL || *path == '\0') - return ((char *)"."); + return (__DECONST(char *, ".")); - /* Retain at least one leading slash character. */ - in = out = *path == '/' ? path + 1 : path; + /* Find end of last pathname component. */ + end = path + strlen(path); + while (end > path + 1 && *(end - 1) == '/') + --end; - skipslash = true; - prev = "."; - prevlen = 1; - for (;;) { - /* Extract the next pathname component. */ - while (*in == '/') - ++in; - begin = in; - while (*in != '/' && *in != '\0') - ++in; - end = in; - if (begin == end) - break; - - /* - * Copy over the previous pathname component, except if - * it's dot. There is no point in retaining those. - */ - if (prevlen != 1 || *prev != '.') { - if (!skipslash) - *out++ = '/'; - skipslash = false; - memmove(out, prev, prevlen); - out += prevlen; - } - - /* Preserve the pathname component for the next iteration. */ - prev = begin; - prevlen = end - begin; - } + /* Strip off the last pathname component. */ + while (end > path && *(end - 1) != '/') + --end; /* * If path does not contain a '/', then dirname() shall return a * pointer to the string ".". */ - if (out == path) - *out++ = '.'; - *out = '\0'; + if (end == path) { + path[0] = '.'; + path[1] = '\0'; + return (path); + } + + /* + * Remove trailing slashes from the resulting directory name. Ensure + * that at least one character remains. + */ + while (end > path + 1 && *(end - 1) == '/') + --end; + + /* Null terminate directory name and return it. */ + *end = '\0'; return (path); }