*** empty log message ***
[gnulib.git] / lib / dirname.c
index 56b66e6..2225800 100644 (file)
@@ -1,5 +1,5 @@
 /* dirname.c -- return all but the last element in a path
-   Copyright (C) 1990, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1990, 1998, 2000 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -32,21 +32,62 @@ char *malloc ();
 #  define strrchr rindex
 # endif
 #endif
+#include <assert.h>
 
-/* Return the leading directories part of PATH,
-   allocated with malloc.  If out of memory, return 0.
-   Assumes that trailing slashes have already been
-   removed.  */
+#ifndef HAVE_DECL_MEMRCHR
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_MEMRCHR
+void *memrchr ();
+#endif
 
-char *
-dirname (path)
-     char *path;
+#include "dirname.h"
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == '/')
+#endif
+
+#define BACKSLASH_IS_PATH_SEPARATOR ISSLASH ('\\')
+
+/* Return the length of `dirname (PATH)' and set *RESULT
+   to point to PATH or to `"."', as appropriate.
+   Works properly even if there are trailing slashes
+   (by effectively ignoring them).  */
+size_t
+dir_name_r (char const *path, char const **result)
 {
-  char *newpath;
-  char *slash;
-  int length;                  /* Length of result, not including NUL.  */
+  char const *slash;
+  size_t length;               /* Length of result, not including NUL.  */
 
   slash = strrchr (path, '/');
+  if (BACKSLASH_IS_PATH_SEPARATOR)
+    {
+      char const *b = strrchr (path, '\\');
+      if (b && slash < b)
+       slash = b;
+    }
+
+  /* If the last byte of PATH is a slash, decrement SLASH until it's
+     pointing at the leftmost in a sequence of trailing slashes.  */
+  if (slash && slash[1] == 0)
+    {
+      while (path < slash && ISSLASH (slash[-1]))
+       {
+         --slash;
+       }
+
+      if (path < slash)
+       {
+         slash = memrchr (path, '/', slash - path);
+         if (BACKSLASH_IS_PATH_SEPARATOR)
+           {
+             char const *b = memrchr (path, '\\', slash - path);
+             if (b && slash < b)
+               slash = b;
+           }
+       }
+    }
+
   if (slash == 0)
     {
       /* File is in the current directory.  */
@@ -56,15 +97,86 @@ dirname (path)
   else
     {
       /* Remove any trailing slashes from the result.  */
-      while (slash > path && *slash == '/')
-       --slash;
+      if (BACKSLASH_IS_PATH_SEPARATOR)
+       {
+         char const *lim = ((path[0] >= 'A' && path[0] <= 'z'
+                             && path[1] == ':')
+                            ? path + 2 : path);
+
+         /* If canonicalized "d:/path", leave alone the root case "d:/".  */
+         while (slash > lim && ISSLASH (*slash))
+           --slash;
+       }
+      else
+       {
+         while (slash > path && ISSLASH (*slash))
+           --slash;
+       }
 
       length = slash - path + 1;
     }
-  newpath = (char *) malloc (length + 1);
+
+  *result = path;
+  return length;
+}
+
+/* Return the leading directories part of PATH,
+   allocated with malloc.  If out of memory, return 0.
+   Works properly even if there are trailing slashes
+   (by effectively ignoring them).  */
+
+char *
+dir_name (char const *path)
+{
+  char const *result;
+  size_t length = dir_name_r (path, &result);
+  char *newpath = (char *) malloc (length + 1);
   if (newpath == 0)
     return 0;
-  strncpy (newpath, path, length);
+  strncpy (newpath, result, length);
   newpath[length] = 0;
   return newpath;
 }
+
+#ifdef TEST_DIRNAME
+/*
+
+Run the test like this (expect no output):
+  gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall memrchr.c dirname.c
+  sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out
+
+BEGIN-DATA
+foo//// .
+bar/foo//// bar
+foo/ .
+/ /
+. .
+a .
+END-DATA
+
+*/
+
+# define MAX_BUFF_LEN 1024
+# include <stdio.h>
+# include <stdlib.h>
+
+int
+main ()
+{
+  char buff[MAX_BUFF_LEN + 1];
+
+  buff[MAX_BUFF_LEN] = 0;
+  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
+    {
+      char path[MAX_BUFF_LEN];
+      char expected_result[MAX_BUFF_LEN];
+      char const *result;
+      sscanf (buff, "%s %s", path, expected_result);
+      result = dir_name (path);
+      if (strcmp (result, expected_result))
+       printf ("%s: got %s, expected %s\n", path, result, expected_result);
+    }
+  exit (0);
+
+}
+#endif