bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: bugs in dirname module - gnulib portion


From: Eric Blake
Subject: Re: bugs in dirname module - gnulib portion
Date: Tue, 22 Nov 2005 07:04:57 -0700
User-agent: Mozilla Thunderbird 1.0.2 (Windows/20050317)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Paul Eggert on 11/21/2005 5:42 PM:

Thanks for the review.  Updated patch attached.

> 
>>-       /* If the file name ends in slash, use the trailing slash as
>>-          the basename if no non-slashes have been found.  */
>>+       /* If the file name ends in slash, but no non-slashes have
>>+          been found, then return the empty string.  */
>>        if (! *p)
>>          {
>>            if (ISSLASH (*base))
>>-             base = p - 1;
>>+             base = p;
>>            break;
>>          }
> 
> 
> That is confusing.  Wouldn't this be a lot simpler, as a replacement
> for the entire main loop in last_component?
> 
>   for (p = base; +p; p++)
>     if (ISSLASH (*p))
>       base = p + 1;

Not quite - base only started as a slash if the name was absolute;
thereafter, it was always assigned to a non-slash character.  This is
important, because trailing slashes must be ignored.  Your simplification
would return "" for something like "/a/", when it should instead return
"a/".  But given your complaint, I simplified the loop of last_component
(and I agree that it is now a lot easier to understand without nested
loops, even though the complexity remains O(n) for either implementation).

> 
> +  /* Collapse a sequence of trailing slashes into one.  */
> +  size_t length = base_len (base);
> 
> This assumes C99 declaration-after-statement.  Let's keep the code
> portable to C89.

Fixed.  I'll have to remember to use -Wdeclaration-after-statement in
addition to -Wall when smoke-screening my code.

>>+  /* On systems with drive letters, `a/b:c' must return `./b:c' rather
>>+     than `b:c' to avoid confusion with a drive letter.  On systems
>>+     with pure POSIX semantics, this is not an issue.  */
>>+  if (FILE_SYSTEM_PREFIX_LEN (base))
>>+    {
>>+      char *p = xmalloc (length + 3);
>>+      p[0] = '.';
>>+      p[1] = DIRECTORY_SEPARATOR;
>>+      memcpy (p + 2, base, length);
>>+      p[length + 2] = '\0';
>>+      return p;
>>+    }
> 
> 
> This mishandles 'a/b:c/', no?  The trailing slash gets dropped.

Added that to the testsuite.  But the trailing slash was preserved by the
earlier line that bumped length if the name had trailing slashes.

> 
> Also, what's the point of DIRECTORY_SEPARATOR?  Can't we just use '/'?

I kept it with DIRECTORY_SEPARATOR, in case the application sets
DIRECTORY_SEPARATOR to '\\' in config.h; after all, systems with drive
letters have users that are used to the backslash.  But I don't have any
problem with hard-coding it to '/' if others agree.  Or should I change it
to check ISSLASH (base[-1]), in which case use the existing spelling?  So
for 'a/b:c', use './b:c', for 'a\b:c', use '.\b:c', and for 'a:b:c', use
'./b:c'?

> 
> 
>>+  if (prefix_len && ! FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE
> 
> 
> This would be easier to read this way:
> 
>   if (FILE_SYSTEM_DRIVE_PREFIX_IS_RELATIVE && prefix_len
> 
> so that it's more obvious that the code is optimized away on normal systems.

OK.  I changed the macro to FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
throughout the code.

> 
>>+  /* Be careful of drive prefixes, where that matters.  */
>>+  if (0 < prefix_length)
>>+    return (prefix_length
>>+         + (! FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE
>>+            && ISSLASH (file[prefix_length])));
>>+
>>+  /* Don't strip the only slash from "/", or from "//" where that matters.  
>>*/
>>+  return (ISSLASH (file[0])
>>+       + (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (file[0])
>>+          && ISSLASH (file[1]) && ! ISSLASH (file[2])));
>> }
> 
> 
> Hmm, a bit hard to read, for the same reason as above.  Also, it tests
> ISSLASH (file[0]) more than once.  How about something like this
> instead, right after initializing prefix_length?  Or perhaps you can
> come up with something better.
> 
>   prefix_length +=
>     (prefix_length != 0
>      ? FILE_SYSTEM_DRIVE_PREFIX_IS_RELATIVE && ISSLASH (file[prefix_length])
>      : ! ISSLASH (file[0])
>      : 0
>      ? (DOUBLE_SLASH_IS_DISTINCT_ROOT
>       && ISSLASH (file[1]) && ! ISSLASH (file[2]))
>      ? 2
>      : 1);
> 

OK, I simplified dir_len based on your idea, so that ISSLASH is used at
most once per character.

ChangeLog:
2005-11-21  Eric Blake  <address@hidden>

        * modules/dirname (Depends-on): Add xstrndup.
        * modules/dirname-tests: New module.
        * tests/test-dirname.c: New file, replacing dirname.c TEST_DIRNAME
        section.


m4/ChangeLog:
2005-11-22  Eric Blake  <address@hidden>

        * dirname.m4 (DOUBLE_SLASH_IS_DISTINCT_ROOT): New define.
        * dos.m4 (FILE_SYSTEM_PREFIX_LEN): Move from here to dirname.h.
        (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE): New define.


lib/ChangeLog:
2005-11-22  Eric Blake  <address@hidden>

        * backupfile.c (check_extension, numbered_backup): Adjust to
        changed semantics in dirname module.
        * filenamecat.c (file_name_concat): Ditto.
        * same.c (same_name): Ditto.

2005-11-22  Eric Blake  <address@hidden>,
            Paul Eggert  <address@hidden>

        * dirname.h (FILE_SYSTEM_PREFIX_LEN): Move here from dos.m4.
        [FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX]: Don't treat 1: as a
        drive prefix.
        (IS_ABSOLUTE_FILE_NAME): Treat all drive letters as absolute on
        platforms like cygwin with FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE.
        (last_component): New method.
        * dirname.c (dir_len): Determine when drive letters need a
        subsequent slash.  Preserve // when it is special.
        (dir_name): Don't append dot when drive letter is absolute.
        [TEST_DIRNAME]: Move into a full-blown gnulib test.
        * basename.c (base_name): New semantics - malloc the result.
        Preserve // when it is special.  Preserve relative files that look
        like drive letters.
        (base_len): Preserve // when it is special.
        (last_component): New method, similar to old base_name semantics.
        * stripslash.c (strip_trailing_slashes): Use last_component, not
        base_name.  Strip redundant slashes from ///.

- --
Life is short - so eat dessert first!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFDgyWI84KuGfSFAYARAu+mAJ9VbAllE/JRMkMwJ3VswbPWqceYJwCfZsVT
PkEyog6tOzOjB5mSJ+1BdgY=
=SP9H
-----END PGP SIGNATURE-----
Index: ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/ChangeLog,v
retrieving revision 1.445
diff -u -p -r1.445 ChangeLog
Index: lib/ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/ChangeLog,v
retrieving revision 1.1051
diff -u -p -r1.1051 ChangeLog
Index: lib/backupfile.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/backupfile.c,v
retrieving revision 1.47
diff -u -p -r1.47 backupfile.c
--- lib/backupfile.c    23 Sep 2005 04:15:13 -0000      1.47
+++ lib/backupfile.c    22 Nov 2005 13:51:24 -0000
@@ -115,7 +115,7 @@ char const *simple_backup_suffix = "~";
 static void
 check_extension (char *file, size_t filelen, char e)
 {
-  char *base = base_name (file);
+  char *base = last_component (file);
   size_t baselen = base_len (base);
   size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
 
@@ -202,7 +202,7 @@ numbered_backup (char **buffer, size_t b
   struct dirent *dp;
   char *buf = *buffer;
   size_t versionlenmax = 1;
-  char *base = base_name (buf);
+  char *base = last_component (buf);
   size_t base_offset = base - buf;
   size_t baselen = base_len (base);
 
Index: lib/basename.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/basename.c,v
retrieving revision 1.26
diff -u -p -r1.26 basename.c
--- lib/basename.c      19 Sep 2005 17:28:14 -0000      1.26
+++ lib/basename.c      22 Nov 2005 13:51:24 -0000
@@ -22,58 +22,107 @@
 #endif
 
 #include "dirname.h"
+
 #include <string.h>
+#include "xalloc.h"
+#include "xstrndup.h"
+
+/* Return the address of the last file name component of NAME.  If
+   NAME has no relative file name components because it is a file
+   system root, return the empty string.  */
+
+char *
+last_component (char const *name)
+{
+  char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
+  char const *p;
+  bool saw_slash = false;
+
+  while (ISSLASH (*base))
+    base++;
+  for (p = base; *p; p++)
+    if (ISSLASH (*p))
+      saw_slash = true;
+    else if (saw_slash)
+      {
+       base = p;
+       saw_slash = false;
+      }
+
+  return (char *) base;
+}
+
 
 /* In general, we can't use the builtin `basename' function if available,
    since it has different meanings in different environments.
    In some environments the builtin `basename' modifies its argument.
 
-   Return the address of the last file name component of NAME.  If
-   NAME has no file name components because it is all slashes, return
-   NAME if it is empty, the address of its last slash otherwise.  */
+   Return the last file name component of NAME, allocated with
+   xmalloc.  On systems with drive letters, a leading "./"
+   distinguishes relative names that would otherwise look like a drive
+   letter.  Unlike POSIX basename(), NAME cannot be NULL,
+   base_name("") returns "", and the first trailing slash is not
+   stripped.
+
+   If lstat (NAME) would succeed, then { chdir (dir_name (NAME));
+   lstat (base_name (NAME)); } will access the same file.  Likewise,
+   if the sequence { chdir (dir_name (NAME));
+   rename (base_name (NAME), "foo"); } succeeds, you have renamed NAME
+   to "foo" in the same directory NAME was in.  */
 
 char *
 base_name (char const *name)
 {
-  char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
-  char const *p;
+  char const *base = last_component (name);
+  size_t length;
 
-  for (p = base; *p; p++)
+  /* If there is no last component, then name is a file system root or the
+     empty string.  */
+  if (! *base)
+    return xstrndup (name, base_len (name));
+
+  /* Collapse a sequence of trailing slashes into one.  */
+  length = base_len (base);
+  if (ISSLASH (base[length]))
+    length++;
+
+  /* On systems with drive letters, `a/b:c' must return `./b:c' rather
+     than `b:c' to avoid confusion with a drive letter.  On systems
+     with pure POSIX semantics, this is not an issue.  */
+  if (FILE_SYSTEM_PREFIX_LEN (base))
     {
-      if (ISSLASH (*p))
-       {
-         /* Treat multiple adjacent slashes like a single slash.  */
-         do p++;
-         while (ISSLASH (*p));
-
-         /* If the file name ends in slash, use the trailing slash as
-            the basename if no non-slashes have been found.  */
-         if (! *p)
-           {
-             if (ISSLASH (*base))
-               base = p - 1;
-             break;
-           }
-
-         /* *P is a non-slash preceded by a slash.  */
-         base = p;
-       }
+      char *p = xmalloc (length + 3);
+      p[0] = '.';
+      p[1] = DIRECTORY_SEPARATOR;
+      memcpy (p + 2, base, length);
+      p[length + 2] = '\0';
+      return p;
     }
 
-  return (char *) base;
+  /* Finally, copy the basename.  */
+  return xstrndup (base, length);
 }
 
-/* Return the length of of the basename NAME.  Typically NAME is the
-   value returned by base_name.  Act like strlen (NAME), except omit
-   redundant trailing slashes.  */
+/* Return the length of the basename NAME.  Typically NAME is the
+   value returned by base_name or last_component.  Act like strlen
+   (NAME), except omit all trailing slashes.  */
 
 size_t
 base_len (char const *name)
 {
   size_t len;
+  size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
 
   for (len = strlen (name);  1 < len && ISSLASH (name[len - 1]);  len--)
     continue;
+
+  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1
+      && ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2])
+    return 2;
+
+  if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len
+      && len == prefix_len && ISSLASH (name[prefix_len]))
+    return prefix_len + 1;
 
   return len;
 }
Index: lib/dirname.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/dirname.c,v
retrieving revision 1.35
diff -u -p -r1.35 dirname.c
--- lib/dirname.c       19 Sep 2005 17:28:14 -0000      1.35
+++ lib/dirname.c       22 Nov 2005 13:51:24 -0000
@@ -26,96 +26,62 @@
 #include <string.h>
 #include "xalloc.h"
 
-/* Return the length of `dirname (FILE)', or zero if FILE is
-   in the working directory.  Works properly even if
-   there are trailing slashes (by effectively ignoring them).  */
+/* Return the length of the prefix of FILE that will be used by
+   dir_name.  If FILE is in the working directory, this returns zero
+   even though `dir_name (FILE)' will return ".".  Works properly even
+   if there are trailing slashes (by effectively ignoring them).  */
+
 size_t
 dir_len (char const *file)
 {
   size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file);
   size_t length;
 
+  /* Advance prefix_length beyond important leading slashes.  */
+  prefix_length += (prefix_length != 0
+                   ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+                      && ISSLASH (file[prefix_length]))
+                   : ISSLASH (file[0])
+                   ? (DOUBLE_SLASH_IS_DISTINCT_ROOT
+                      && ISSLASH (file[1]) && ! ISSLASH (file[2])
+                      ? 2 : 1)
+                   : 0);
+
   /* Strip the basename and any redundant slashes before it.  */
-  for (length = base_name (file) - file;  prefix_length < length;  length--)
+  for (length = last_component (file) - file;
+       prefix_length < length; length--)
     if (! ISSLASH (file[length - 1]))
-      return length;
-
-  /* But don't strip the only slash from "/".  */
-  return prefix_length + ISSLASH (file[prefix_length]);
+      break;
+  return length;
 }
 
-/* Return the leading directories part of FILE,
-   allocated with xmalloc.
-   Works properly even if there are trailing slashes
-   (by effectively ignoring them).  */
+
+/* In general, we can't use the builtin `dirname' function if available,
+   since it has different meanings in different environments.
+   In some environments the builtin `dirname' modifies its argument.
+
+   Return the leading directories part of FILE, allocated with xmalloc.
+   Works properly even if there are trailing slashes (by effectively
+   ignoring them).  Unlike POSIX dirname(), FILE cannot be NULL.
+
+   If lstat (FILE) would succeed, then { chdir (dir_name (FILE));
+   lstat (base_name (FILE)); } will access the same file.  Likewise,
+   if the sequence { chdir (dir_name (FILE));
+   rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE
+   to "foo" in the same directory FILE was in.  */
 
 char *
 dir_name (char const *file)
 {
   size_t length = dir_len (file);
-  bool append_dot = (length == FILE_SYSTEM_PREFIX_LEN (file));
+  bool append_dot = (length == 0
+                    || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+                        && length == FILE_SYSTEM_PREFIX_LEN (file)
+                        && file[2] != '\0' && ! ISSLASH (file[2])));
   char *dir = xmalloc (length + append_dot + 1);
   memcpy (dir, file, length);
   if (append_dot)
     dir[length++] = '.';
-  dir[length] = 0;
+  dir[length] = '\0';
   return dir;
 }
-
-#ifdef TEST_DIRNAME
-/*
-
-Run the test like this (expect no output):
-  gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall \
-     basename.c dirname.c xmalloc.c error.c
-  sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out
-
-If it's been built on a DOS or Windows platforms, run another test like
-this (again, expect no output):
-  sed -n '/^BEGIN-DOS-DATA$/,/^END-DOS-DATA$/p' dirname.c|grep -v DATA|./a.out
-
-BEGIN-DATA
-foo//// .
-bar/foo//// bar
-foo/ .
-/ /
-. .
-a .
-END-DATA
-
-BEGIN-DOS-DATA
-c:///// c:/
-c:/ c:/
-c:/. c:/
-c:foo c:.
-c:foo/bar c:foo
-END-DOS-DATA
-
-*/
-
-# define MAX_BUFF_LEN 1024
-# include <stdio.h>
-
-char *program_name;
-
-int
-main (int argc, char *argv[])
-{
-  char buff[MAX_BUFF_LEN + 1];
-
-  program_name = argv[0];
-
-  buff[MAX_BUFF_LEN] = 0;
-  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
-    {
-      char file[MAX_BUFF_LEN];
-      char expected_result[MAX_BUFF_LEN];
-      char const *result;
-      sscanf (buff, "%s %s", file, expected_result);
-      result = dir_name (file);
-      if (strcmp (result, expected_result))
-       printf ("%s: got %s, expected %s\n", file, result, expected_result);
-    }
-  return 0;
-}
-#endif
Index: lib/dirname.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/dirname.h,v
retrieving revision 1.11
diff -u -p -r1.11 dirname.h
--- lib/dirname.h       2 Jun 2005 20:41:05 -0000       1.11
+++ lib/dirname.h       22 Nov 2005 13:51:24 -0000
@@ -31,16 +31,39 @@
 # endif
 
 # ifndef FILE_SYSTEM_PREFIX_LEN
-#  define FILE_SYSTEM_PREFIX_LEN(File_name) 0
+#  if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX
+    /* This internal macro assumes ASCII, but all hosts that support drive
+       letters use ASCII.  */
+#   define _IS_DRIVE_LETTER(c) (((unsigned int) (c) | ('a' - 'A')) - 'a' \
+                               <= 'z' - 'a')
+#   define FILE_SYSTEM_PREFIX_LEN(Filename) \
+          (_IS_DRIVE_LETTER ((Filename)[0]) && (Filename)[1] == ':' ? 2 : 0)
+#  else
+#   define FILE_SYSTEM_PREFIX_LEN(Filename) 0
+#  endif
 # endif
 
-# define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)])
+# ifndef FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+#  define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0
+# endif
+
+# ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
+#  define DOUBLE_SLASH_IS_DISTINCT_ROOT 1
+# endif
+
+# if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+#  define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)])
+# else
+#  define IS_ABSOLUTE_FILE_NAME(F) \
+         (ISSLASH ((F)[0]) || 0 < FILE_SYSTEM_PREFIX_LEN (F))
+# endif
 # define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F))
 
 char *base_name (char const *file);
 char *dir_name (char const *file);
 size_t base_len (char const *file);
 size_t dir_len (char const *file);
+char *last_component (char const *file);
 
 bool strip_trailing_slashes (char *file);
 
Index: lib/filenamecat.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/filenamecat.c,v
retrieving revision 1.2
diff -u -p -r1.2 filenamecat.c
--- lib/filenamecat.c   19 Sep 2005 17:28:14 -0000      1.2
+++ lib/filenamecat.c   22 Nov 2005 13:51:24 -0000
@@ -64,7 +64,7 @@ longest_relative_suffix (char const *f)
 char *
 file_name_concat (char const *dir, char const *abase, char **base_in_result)
 {
-  char const *dirbase = base_name (dir);
+  char const *dirbase = last_component (dir);
   size_t dirbaselen = base_len (dirbase);
   size_t dirlen = dirbase - dir + dirbaselen;
   size_t needs_separator = (dirbaselen && ! ISSLASH (dirbase[dirbaselen - 1]));
Index: lib/same.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/same.c,v
retrieving revision 1.19
diff -u -p -r1.19 same.c
--- lib/same.c  23 Sep 2005 04:15:13 -0000      1.19
+++ lib/same.c  22 Nov 2005 13:51:24 -0000
@@ -59,8 +59,8 @@ bool
 same_name (const char *source, const char *dest)
 {
   /* Compare the basenames.  */
-  char const *source_basename = base_name (source);
-  char const *dest_basename = base_name (dest);
+  char const *source_basename = last_component (source);
+  char const *dest_basename = last_component (dest);
   size_t source_baselen = base_len (source_basename);
   size_t dest_baselen = base_len (dest_basename);
   bool identical_basenames =
Index: lib/stripslash.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/stripslash.c,v
retrieving revision 1.15
diff -u -p -r1.15 stripslash.c
--- lib/stripslash.c    19 Sep 2005 17:28:15 -0000      1.15
+++ lib/stripslash.c    22 Nov 2005 13:51:24 -0000
@@ -22,19 +22,26 @@
 
 #include "dirname.h"
 
-/* Remove trailing slashes from FILE.
-   Return true if a trailing slash was removed.
-   This is useful when using file name completion from a shell that
-   adds a "/" after directory names (such as tcsh and bash), because
-   the Unix rename and rmdir system calls return an "Invalid argument" error
-   when given a file that ends in "/" (except for the root directory).  */
+/* Remove trailing slashes from FILE.  Return true if a trailing slash
+   was removed.  This is useful when using file name completion from a
+   shell that adds a "/" after directory names (such as tcsh and
+   bash), because on symlinks to directories, several system calls
+   have different semantics according to whether a trailing slash is
+   present.  */
 
 bool
 strip_trailing_slashes (char *file)
 {
-  char *base = base_name (file);
-  char *base_lim = base + base_len (base);
-  bool had_slash = (*base_lim != '\0');
+  char *base = last_component (file);
+  char *base_lim;
+  bool had_slash;
+
+  /* last_component returns "" for file system roots, but we need to turn
+     `///' into `/'.  */
+  if (! *base)
+    base = file;
+  base_lim = base + base_len (base);
+  had_slash = (*base_lim != '\0');
   *base_lim = '\0';
   return had_slash;
 }
Index: m4/ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/ChangeLog,v
retrieving revision 1.758
diff -u -p -r1.758 ChangeLog
Index: m4/dirname.m4
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/dirname.m4,v
retrieving revision 1.8
diff -u -p -r1.8 dirname.m4
--- m4/dirname.m4       21 Mar 2005 22:06:27 -0000      1.8
+++ m4/dirname.m4       22 Nov 2005 13:51:25 -0000
@@ -1,4 +1,4 @@
-# dirname.m4 serial 5
+# dirname.m4 serial 6
 dnl Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -11,6 +11,28 @@ AC_DEFUN([gl_DIRNAME],
 
   dnl Prerequisites of lib/dirname.h.
   AC_REQUIRE([gl_AC_DOS])
+  AC_CACHE_CHECK([whether // is distinct from /], [ac_cv_double_slash_root],
+    [ if test x"$cross_compiling" = xyes ; then
+       # When cross-compiling, there is no way to tell whether // is special
+       # short of a list of hosts; so always treat it as special.
+       ac_cv_double_slash_root=unknown
+      else
+       set x `ls -di / //`
+       if test $[2] = $[4]; then
+         ac_cv_double_slash_root=no
+       else
+         ac_cv_double_slash_root=yes
+       fi
+      fi])
+  if test x"$ac_cv_double_slash_root" = xno; then
+    ac_double_slash_root=0
+  else
+    ac_double_slash_root=1
+  fi
+
+  AC_DEFINE_UNQUOTED([DOUBLE_SLASH_IS_DISTINCT_ROOT],
+   $ac_double_slash_root,
+   [Define to 1 if // is a file system root distinct from /.])
 
   dnl No prerequisites of lib/basename.c, lib/dirname.c, lib/stripslash.c.
 ])
Index: m4/dos.m4
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/dos.m4,v
retrieving revision 1.12
diff -u -p -r1.12 dos.m4
--- m4/dos.m4   23 Jan 2005 08:06:57 -0000      1.12
+++ m4/dos.m4   22 Nov 2005 13:51:25 -0000
@@ -1,9 +1,9 @@
-#serial 9
+#serial 10
 
 # Define some macros required for proper operation of code in lib/*.c
 # on MSDOS/Windows systems.
 
-# Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc.
+# Copyright (C) 2000, 2001, 2004, 2005 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -14,30 +14,38 @@ AC_DEFUN([gl_AC_DOS],
   [
     AC_CACHE_CHECK([whether system is Windows or MSDOS], [ac_cv_win_or_dos],
       [
-        AC_TRY_COMPILE([],
-        [#if !defined _WIN32 && !defined __WIN32__ && !defined __MSDOS__ && 
!defined __CYGWIN__
+       AC_TRY_COMPILE([],
+       [#if !defined _WIN32 && !defined __WIN32__ && !defined __MSDOS__ && 
!defined __CYGWIN__
 neither MSDOS nor Windows
 #endif],
-        [ac_cv_win_or_dos=yes],
-        [ac_cv_win_or_dos=no])
+       [ac_cv_win_or_dos=yes],
+       [ac_cv_win_or_dos=no])
       ])
 
     if test x"$ac_cv_win_or_dos" = xyes; then
       ac_fs_accepts_drive_letter_prefix=1
       ac_fs_backslash_is_file_name_separator=1
+      AC_CACHE_CHECK([whether drive letter can start relative path],
+                    [ac_cv_drive_letter_can_be_relative],
+       [
+         AC_TRY_COMPILE([],
+         [#if defined __CYGWIN__
+drive letters are always absolute
+#endif],
+         [ac_cv_drive_letter_can_be_relative=yes],
+         [ac_cv_drive_letter_can_be_relative=no])
+       ])
+      if test x"$ac_cv_drive_letter_can_be_relative" = xyes; then
+       ac_fs_drive_letter_can_be_relative=1
+      else
+       ac_fs_drive_letter_can_be_relative=0
+      fi
     else
       ac_fs_accepts_drive_letter_prefix=0
       ac_fs_backslash_is_file_name_separator=0
+      ac_fs_drive_letter_can_be_relative=0
     fi
 
-    AH_VERBATIM(FILE_SYSTEM_PREFIX_LEN,
-    [#if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX
-# define FILE_SYSTEM_PREFIX_LEN(Filename) \
-  ((Filename)[0] && (Filename)[1] == ':' ? 2 : 0)
-#else
-# define FILE_SYSTEM_PREFIX_LEN(Filename) 0
-#endif])
-
     AC_DEFINE_UNQUOTED([FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX],
       $ac_fs_accepts_drive_letter_prefix,
       [Define on systems for which file names may have a so-called
@@ -55,4 +63,9 @@ neither MSDOS nor Windows
       $ac_fs_backslash_is_file_name_separator,
       [Define if the backslash character may also serve as a file name
        component separator.])
+
+    AC_DEFINE_UNQUOTED([FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE],
+      $ac_fs_drive_letter_can_be_relative,
+      [Define if a drive letter prefix denotes a relative path if it is
+       not followed by a file name component separator.])
   ])
Index: modules/dirname
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/dirname,v
retrieving revision 1.6
diff -u -p -r1.6 dirname
--- modules/dirname     6 May 2005 17:22:45 -0000       1.6
+++ modules/dirname     22 Nov 2005 13:51:25 -0000
@@ -6,12 +6,13 @@ lib/dirname.h
 lib/dirname.c
 lib/basename.c
 lib/stripslash.c
-m4/dos.m4
 m4/dirname.m4
+m4/dos.m4
 
 Depends-on:
-xalloc
 stdbool
+xalloc
+xstrndup
 
 configure.ac:
 gl_DIRNAME
Index: modules/dirname-tests
===================================================================
RCS file: modules/dirname-tests
diff -N modules/dirname-tests
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ modules/dirname-tests       22 Nov 2005 13:51:25 -0000
@@ -0,0 +1,12 @@
+Files:
+tests/test-dirname.c
+
+Depends-on:
+strdup
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-dirname
+noinst_PROGRAMS += test-dirname
+test_dirname_SOURCES = test-dirname.c
Index: tests/test-dirname.c
===================================================================
RCS file: tests/test-dirname.c
diff -N tests/test-dirname.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/test-dirname.c        22 Nov 2005 13:51:25 -0000
@@ -0,0 +1,196 @@
+/* Test the gnulib dirname module.
+   Copyright (C) 2005 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
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "dirname.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "strdup.h"
+
+const char *program_name = "test-dirname";
+
+struct test {
+  const char *name;    /* Name under test.  */
+  const char *dir;     /* dir_name (name).  */
+  const char *last;    /* last_component (name).  */
+  const char *base;    /* base_name (name).  */
+  const char *stripped;        /* name after strip_trailing_slashes (name).  */
+  bool modified;        /* result of strip_trailing_slashes (name).  */
+  bool absolute;       /* IS_ABSOLUTE_FILE_NAME (name).  */
+};
+
+static struct test tests[] = {
+  {"d/f",      "d",    "f",    "f",    "d/f",  false,  false},
+  {"/d/f",     "/d",   "f",    "f",    "/d/f", false,  true},
+  {"d/f/",     "d",    "f/",   "f/",   "d/f",  true,   false},
+  {"d/f//",    "d",    "f//",  "f/",   "d/f",  true,   false},
+  {"f",                ".",    "f",    "f",    "f",    false,  false},
+  {"/",                "/",    "",     "/",    "/",    false,  true},
+#if DOUBLE_SLASH_IS_DISTINCT_ROOT
+  {"//",       "//",   "",     "//",   "//",   false,  true},
+  {"//d",      "//",   "d",    "d",    "//d",  false,  true},
+#else
+  {"//",       "/",    "",     "/",    "/",    true,   true},
+  {"//d",      "/",    "d",    "d",    "//d",  false,  true},
+#endif
+  {"///",      "/",    "",     "/",    "/",    true,   true},
+  {"///a///",  "/",    "a///", "a/",   "///a", true,   true},
+  /* POSIX requires dirname("") and basename("") to both return ".",
+     but dir_name and base_name are defined differently.  */
+  {"",         ".",    "",     "",     "",     false,  false},
+  {".",                ".",    ".",    ".",    ".",    false,  false},
+  {"..",       ".",    "..",   "..",   "..",   false,  false},
+#if FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR
+  {"a\\",      ".",    "a\\",  "a\\",  "a",    true,   false},
+  {"a\\b",     "a",    "b",    "b",    "a\\b", false,  false},
+  {"\\",       "\\",   "",     "\\",   "\\",   false,  true},
+  {"\\/\\",    "\\",   "",     "\\",   "\\",   true,   true},
+  {"\\\\/",    "\\",   "",     "\\",   "\\",   true,   true},
+  {"\\//",     "\\",   "",     "\\",   "\\",   true,   true},
+  {"//\\",     "/",    "",     "/",    "/",    true,   true},
+#else
+  {"a\\",      ".",    "a\\",  "a\\",  "a\\",  false,  false},
+  {"a\\b",     ".",    "a\\b", "a\\b", "a\\b", false,  false},
+  {"\\",       ".",    "\\",   "\\",   "\\",   false,  false},
+  {"\\/\\",    "\\",   "\\",   "\\",   "\\/\\",false,  false},
+  {"\\\\/",    ".",    "\\\\/","\\\\/","\\\\", true,   false},
+  {"\\//",     ".",    "\\//", "\\/",  "\\",   true,   false},
+# if DOUBLE_SLASH_IS_DISTINCT_ROOT
+  {"//\\",     "//",   "\\",   "\\",   "//\\", false,  true},
+# else
+  {"//\\",     "/",    "\\",   "\\",   "//\\", false,  true},
+# endif
+#endif
+#if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX
+# if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+  {"c:",       "c:",   "",     "c:",   "c:",   false,  false},
+  {"c:/",      "c:/",  "",     "c:/",  "c:/",  false,  true},
+  {"c://",     "c:/",  "",     "c:/",  "c:/",  true,   true},
+  {"c:/d",     "c:/",  "d",    "d",    "c:/d", false,  true},
+  {"c://d",    "c:/",  "d",    "d",    "c://d",false,  true},
+  {"c:/d/",    "c:/",  "d/",   "d/",   "c:/d", true,   true},
+  {"c:/d/f",   "c:/d", "f",    "f",    "c:/d/f",false, true},
+  {"c:d",      "c:.",  "d",    "d",    "c:d",  false,  false},
+  {"c:d/",     "c:.",  "d/",   "d/",   "c:d",  true,   false},
+  {"c:d/f",    "c:d",  "f",    "f",    "c:d/f",false,  false},
+  {"a:b:c",    "a:.",  "b:c",  "./b:c","a:b:c",false,  false},
+  {"a/b:c",    "a",    "b:c",  "./b:c","a/b:c",false,  false},
+  {"a/b:c/",   "a",    "b:c/", "./b:c/","a/b:c",true,  false},
+# else /* ! FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE */
+  {"c:",       "c:",   "",     "c:",   "c:",   false,  true},
+  {"c:/",      "c:",   "",     "c:",   "c:",   true,   true},
+  {"c://",     "c:",   "",     "c:",   "c:",   true,   true},
+  {"c:/d",     "c:",   "d",    "d",    "c:/d", false,  true},
+  {"c://d",    "c:",   "d",    "d",    "c://d",false,  true},
+  {"c:/d/",    "c:",   "d/",   "d/",   "c:/d", true,   true},
+  {"c:/d/f",   "c:/d", "f",    "f",    "c:/d/f",false, true},
+  {"c:d",      "c:",   "d",    "d",    "c:d",  false,  true},
+  {"c:d/",     "c:",   "d/",   "d/",   "c:d",  true,   true},
+  {"c:d/f",    "c:d",  "f",    "f",    "c:d/f",false,  true},
+  {"a:b:c",    "a:",   "b:c",  "./b:c","a:b:c",false,  true},
+  {"a/b:c",    "a",    "b:c",  "./b:c","a/b:c",false,  false},
+  {"a/b:c/",   "a",    "b:c/", "./b:c/","a/b:c",true,  false},
+# endif
+#else /* ! FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX */
+  {"c:",       ".",    "c:",   "c:",   "c:",   false,  false},
+  {"c:/",      ".",    "c:/",  "c:/",  "c:",   true,   false},
+  {"c://",     ".",    "c://", "c:/",  "c:",   true,   false},
+  {"c:/d",     "c:",   "d",    "d",    "c:/d", false,  false},
+  {"c://d",    "c:",   "d",    "d",    "c://d",false,  false},
+  {"c:/d/",    "c:",   "d/",   "d/",   "c:/d", true,   false},
+  {"c:/d/f",   "c:/d", "f",    "f",    "c:/d/f",false, false},
+  {"c:d",      ".",    "c:d",  "c:d",  "c:d",  false,  false},
+  {"c:d/",     ".",    "c:d/", "c:d/", "c:d",  true,   false},
+  {"c:d/f",    "c:d",  "f",    "f",    "c:d/f",false,  false},
+  {"a:b:c",    ".",    "a:b:c","a:b:c","a:b:c",false,  false},
+  {"a/b:c",    "a",    "b:c",  "b:c",  "a/b:c",false,  false},
+  {"a/b:c/",   "a",    "b:c/", "b:c/", "a/b:c",true,   false},
+#endif
+  {"1:",       ".",    "1:",   "1:",   "1:",   false,  false},
+  {"1:/",      ".",    "1:/",  "1:/",  "1:",   true,   false},
+  {"/:",       "/",    ":",    ":",    "/:",   false,  true},
+  {"/:/",      "/",    ":/",   ":/",   "/:",   true,   true},
+  /* End sentinel.  */
+  {NULL,       NULL,   NULL,   NULL,   NULL,   false,  false}
+};
+
+int
+main ()
+{
+  struct test *t;
+  bool ok = true;
+
+  for (t = tests; t->name; t++)
+    {
+      char *dir = dir_name (t->name);
+      int dirlen = dir_len (t->name);
+      char *last = last_component (t->name);
+      char *base = base_name (t->name);
+      int baselen = base_len (base);
+      char *stripped = strdup (t->name);
+      bool modified = strip_trailing_slashes (stripped);
+      bool absolute = IS_ABSOLUTE_FILE_NAME (t->name);
+      if (! (strcmp (dir, t->dir) == 0
+            && (dirlen == strlen (dir)
+                || (dirlen + 1 == strlen (dir) && dir[dirlen] == '.'))))
+       {
+         ok = false;
+         printf ("dir_name `%s': got `%s' len %d, expected `%s' len %d\n",
+                 t->name, dir, dirlen, t->dir, strlen (t->dir));
+       }
+      if (strcmp (last, t->last))
+       {
+         ok = false;
+         printf ("last_component `%s': got `%s', expected `%s'\n",
+                 t->name, last, t->last);
+       }
+      if (! (strcmp (base, t->base) == 0
+            && (baselen == strlen (base)
+                || (baselen + 1 == strlen (base)
+                    && ISSLASH (base[baselen])))))
+       {
+         ok = false;
+         printf ("base_name `%s': got `%s' len %d, expected `%s' len %d\n",
+                 t->name, base, baselen, t->base, strlen (t->base));
+       }
+      if (strcmp (stripped, t->stripped) || modified != t->modified)
+       {
+         ok = false;
+         printf ("strip_trailing_slashes `%s': got %s %s, expected %s %s\n",
+                 t->name, stripped, modified ? "changed" : "unchanged",
+                 t->stripped, t->modified ? "changed" : "unchanged");
+       }
+      if (t->absolute != absolute)
+       {
+         ok = false;
+         printf ("`%s': got %s, expected %s\n", t->name,
+                 absolute ? "absolute" : "relative",
+                 t->absolute ? "absolute" : "relative");
+       }
+      free (dir);
+      free (base);
+      free (stripped);
+    }
+  return ok ? 0 : 1;
+}

reply via email to

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