bug-glibc
[Top][All Lists]
Advanced

[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

Attachment: t_dirname.c
Description: Binary data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]