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
This commit is contained in:
Ed Schouten 2016-09-18 20:47:55 +00:00
parent 06403dbce5
commit fd85bff53e
2 changed files with 25 additions and 47 deletions

View File

@ -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 ,

View File

@ -27,64 +27,47 @@
__FBSDID("$FreeBSD$");
#include <libgen.h>
#include <stdbool.h>
#include <string.h>
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);
}