[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Bug in dirname(3)
From: |
Michael Kerrisk |
Subject: |
Bug in dirname(3) |
Date: |
Wed, 13 Dec 2000 08:02:54 +0100 |
Hello,
This email is to report a bug in dirname(3) (misc/dirname.c).
Scenario: dirname("/usr/") should return "/" (as per SUSv2), but instead
returns "/usr/" (in glibc 2.2.5) or "" (empty string, in glibc 2.1.3).
This error is not detected because misc/tst-dirname.c falsely tests the
case "/usr" instead of "/usr/" (as per SUSv2).
There also seem to be other errors in the 2.2.5 implementation of
dirname(3). For example dirname("/.///aaaaa") should (in my reading of
SUSv2) give "/.", which is what dirname(1) does indeed give. dirname(3)
in glibc 2.2.52 gives "/.//".
I have included a test program which includes and tests copies of the
2.1.3 and 2.2.5 versions of dirname(), as well as my own version which I
belive is SUSv2 compliant: dirname_mtk() in the attached source.
Finally, on my Linux distro (SuSE 6.4), there are no man pages for
dirname(3) and basename(3). I seem to recall a while ago seeing a note on
the GNU site asking for volunteers to write them. If this is still
required, let me know and I will write them.
Cheers,
Michael
-----------------------------
/* t_dirname.c
Copyright Michael Kerrisk, Sep 00
Test versions of dirname(3)
Usage: t_dirname [path...]
If pathnames are not provided on command line, then tests are run
using a standard set of pathnames
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>
/*#** GLIB 2.1.3 dirname - shown here - seems to be broken.
e.g. dirname("/usr/") should (according to SVR4) give "/", but it
actually gives "." */
char *
dirname_2_1_3 (char *path)
{
static const char dot[] = ".";
char *last_slash;
/* Find last '/'. */
last_slash = path != NULL ? strrchr (path, '/') : NULL;
if (last_slash == path)
/* The last slash is the first character in the string. We have to
return "/". */
++last_slash;
else if (last_slash != NULL && last_slash[1] == '\0') {
/* The '/' is the last character, we have to look further. */
/* BUG: last_slash = memchr (path, last_slash - path, '/'); */
last_slash = memchr (path, '/', last_slash - path);
}
if (last_slash != NULL)
/* Terminate the path. */
last_slash[0] = '\0';
else
/* This assignment is ill-designed but the XPG specs require to
return a string containing "." in any case no directory part is
found and so a static and constant string is required. */
path = (char *) dot;
return path;
}
char *
dirname_2_2_5 (char *path)
{
static const char dot[] = ".";
char *last_slash;
/* Find last '/'. */
last_slash = path != NULL ? strrchr (path, '/') : NULL;
if (last_slash == path)
/* The last slash is the first character in the string. We have to
return "/". */
++last_slash;
else if (last_slash != NULL && last_slash[1] == '\0')
/* The '/' is the last character, we have to look further. */
last_slash = memchr (path, last_slash - path, '/');
if (last_slash != NULL)
/* Terminate the path. */
last_slash[0] = '\0';
else
/* This assignment is ill-designed but the XPG specs require to
return a string containing "." in any case no directory part is
found and so a static and constant string is required. */
path = (char *) dot;
return path;
}
/*#** MTK version of dirname */
char *
dirname_mtk (char *path)
{
static const char dot[] = ".";
char *last_slash;
if (path == NULL || *path == '\0') {
/* NULL path or empty string: return "." */
path = (char *) dot;
} else {
last_slash = path + strlen(path) - 1;
/* Ignore multiple slashes at end of path, step back until we find
a non-slash, or we reach start of string */
while (*last_slash == '/' && last_slash > path)
--last_slash;
if (*last_slash == '/') /* We got to BOS and found no non-slash */
/* Pathname consisted only of slashes, return "/" */
path[1] = '\0';
else {
/* Walk backwards to find slash */
while (*last_slash != '/' && last_slash > path)
last_slash --;
if (*last_slash == '/') { /* We found a slash */
if (last_slash == path) /* But it was the first char */
path[1] = '\0'; /* So return "/" */
else { /* Slash is not first char, so... */
/* may have multiple slashes at end of directory spec,
so walk back till we reach a non-slash */
while (*(last_slash-1) == '/' && last_slash > path)
last_slash --;
*last_slash = '\0'; /* dirname ends here */
}
} else {
path = (char *) dot; /* No slash found, return "." */
}
}
}
return path;
}
void
test_all(char *path)
{
char *dir, *tmp1, *tmp2;
printf("%s\n", path);
tmp1 = strdup(path);
tmp2 = strdup(path);
dir = dirname_2_1_3(tmp2);
printf("\t%s (2.1.3)\n", dir);
tmp2 = strdup(path);
dir = dirname_2_2_5(tmp2);
printf("\t%s (2.2.5)\n", dir);
tmp2 = strdup(path);
dir = dirname_mtk(tmp2);
printf("\t%s (mtk)\n", dir);
free(tmp1);
free(tmp2);
}
int
main(int argc, char *argv[])
{
int j;
if (argc > 1) {
for (j =1; j < argc; j++)
test_all(argv[j]);
} else {
/* SUSv2 tests */
test_all("/usr/lib");
test_all("/usr/");
test_all("usr");
test_all("/");
test_all(".");
test_all("..");
/* Other tests */
test_all("/.///aaa");
test_all("/usr");
}
exit(EXIT_SUCCESS);
} /* main */
__________________________________________
Michael Kerrisk
mailto: address@hidden
The following section of this message contains a file attachment
prepared for transmission using the Internet MIME message format.
If you are using Pegasus Mail, or any another MIME-compliant system,
you should be able to save it or view it from within your mailer.
If you cannot, please ask your system administrator for assistance.
---- File information -----------
File: t_dirname.c
Date: 12 Dec 2000, 22:57
Size: 4450 bytes.
Type: Program-source
t_dirname.c
Description: Binary data
- Bug in dirname(3),
Michael Kerrisk <=