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:
parent
06403dbce5
commit
fd85bff53e
@ -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 ,
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user