bug-coreutils
[Top][All Lists]
Advanced

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

Re: bugs in dirname module - coreutils portion


From: Eric Blake
Subject: Re: bugs in dirname module - coreutils portion
Date: Wed, 23 Nov 2005 12:21:02 -0700
User-agent: Mozilla Thunderbird 1.0.2 (Windows/20050317)

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

This is my patch to coreutils to match the dirname module updates thread
on gnulib: http://lists.gnu.org/archive/html/bug-gnulib/2005-11/msg00071.html

I've also fixed the two nits that Paul had with the latest version of my
gnulib patch, mentioned in
http://lists.gnu.org/archive/html/bug-gnulib/2005-11/msg00076.html.

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

        * tests/dirname/basic: New file.
        * tests/dirname/Makefile.am: New file.
        * tests/basename/basic: Add some tests.
        * tests/Makefile.am (SUBDIRS): Add dirname tests.
        * configure.ac (AC_CONFIG_FILES): Likewise.

        Improvements to dirname/basename handling on platforms like
        cygwin with distinct // and with drive letters.
        * src/basename.c (main): Don't strip suffix from file system
        roots.
        * src/cp.c (target_directory_operand): Use new last_component.
        (ASSIGN_BASENAME_STRDUPA): Likewise.  Reduce time spent
        traversing the string.
        * src/dircolors.c (guess_shell_syntax): Use new last_component.
        * src/install.c (target_directory_operand, install_file_in_dir):
        Likewise.
        * src/ln.c (target_directory_operand, main): Likewise.
        * src/ls.c (basename_is_dot_or_dotdot): Likewise.
        * src/mv.c (target_directory_operand, movefile): Likewise.
        * src/remove.c (rm_1): Likewise.
        * src/rm.c (usage): Clean up after base_name.
        * src/shred.c (wipename): Use new last_component.
        * src/split.c (next_file_name): Likewise.
        * src/su.c (log_su, run_shell): Likewise.


doc/ChangeLog:
2005-11-23  Eric Blake  <address@hidden>

        * coreutils.texi (basename invocation, dirname invocation):
        Improve documentation to match recent patches.
        (paste invocation): Fix whitespace.


lib/ChangeLog: (merge from gnulib)
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 ///.


m4/ChangeLog: (merge from gnulib)
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.

- --
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

iD8DBQFDhMEd84KuGfSFAYARAnQ4AJ4qwtTTy0p5DhfyDDkyoL9VuFnC8QCguDMo
5R+9bPAaEkv3a0YF2lmlM5I=
=9jCf
-----END PGP SIGNATURE-----
Index: configure.ac
===================================================================
RCS file: /cvsroot/coreutils/coreutils/configure.ac,v
retrieving revision 1.74
diff -u -p -r1.74 configure.ac
--- configure.ac        22 Nov 2005 15:32:20 -0000      1.74
+++ configure.ac        23 Nov 2005 18:43:43 -0000
@@ -235,7 +235,7 @@ if test $jm_cv_sys_tiocgwinsz_needs_term
 
   test $su_cv_sys_tiocgwinsz_in_sys_pty_h = yes \
       && AC_DEFINE(GWINSZ_IN_SYS_PTY, 1,
-                   [Define if your system defines TIOCGWINSZ in sys/pty.h.])
+                  [Define if your system defines TIOCGWINSZ in sys/pty.h.])
 fi
 
 # For src/kill.c.
@@ -267,6 +267,7 @@ AC_CONFIG_FILES(
   tests/cut/Makefile
   tests/dd/Makefile
   tests/dircolors/Makefile
+  tests/dirname/Makefile
   tests/du/Makefile
   tests/expr/Makefile
   tests/factor/Makefile
Index: tests/dirname/Makefile.am
===================================================================
RCS file: tests/dirname/Makefile.am
diff -N tests/dirname/Makefile.am
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/dirname/Makefile.am   23 Nov 2005 18:43:43 -0000
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in -*-Makefile-*-.
+AUTOMAKE_OPTIONS = 1.4 gnits
+
+TESTS = basic
+EXTRA_DIST = $(TESTS)
+TESTS_ENVIRONMENT = \
+  top_srcdir=$(top_srcdir) \
+  srcdir=$(srcdir) \
+  PERL="$(PERL)" \
+  PATH="`pwd`/../../src$(PATH_SEPARATOR)$$PATH" \
+  PROG=dirname
Index: tests/dirname/basic
===================================================================
RCS file: tests/dirname/basic
diff -N tests/dirname/basic
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/dirname/basic 23 Nov 2005 18:43:43 -0000
@@ -0,0 +1,71 @@
+#!/bin/sh
+# -*-perl-*-
+
+: ${PERL=perl}
+: ${srcdir=.}
+
+$PERL -e 1 > /dev/null 2>&1 || {
+  echo 1>&2 "$0: configure didn't find a usable version of Perl," \
+    "so can't run this test"
+  exit 77
+}
+
+d=$srcdir/..
+exec $PERL -w -I$d -MCoreutils -- - << \EOF
+require 5.003;
+use strict;
+use File::stat;
+
+(my $program_name = $0) =~ s|.*/||;
+
+# Turn off localisation of executable's ouput.
address@hidden(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+my $stat_single = stat('/');
+my $stat_double = stat('//');
+my $double_slash = ($stat_single->dev == $stat_double->dev
+                   && $stat_single->ino == $stat_double->ino) ? '/' : '//';
+
+my $prog = $ENV{PROG} || die "$0: \$PROG not specified in environment\n";
+
+my @Tests =
+    (
+     ['fail-1', {ERR => "$prog: missing operand\n"
+       . "Try `$prog --help' for more information.\n"}, {EXIT => '1'}],
+     ['fail-2', qw(a b), {ERR => "$prog: extra operand `b'\n"
+       . "Try `$prog --help' for more information.\n"}, {EXIT => '1'}],
+
+     ['a', qw(d/f),        {OUT => 'd'}],
+     ['b', qw(/d/f),       {OUT => '/d'}],
+     ['c', qw(d/f/),       {OUT => 'd'}],
+     ['d', qw(d/f//),      {OUT => 'd'}],
+     ['e', qw(f),          {OUT => '.'}],
+     ['f', qw(/),          {OUT => '/'}],
+     ['g', qw(//),         {OUT => "$double_slash"}],
+     ['h', qw(///),        {OUT => '/'}],
+     ['i', qw(//a//),      {OUT => "$double_slash"}],
+     ['j', qw(///a///),    {OUT => '/'}],
+     ['k', qw(///a///b),   {OUT => '///a'}],
+     ['l', qw(///a//b/),   {OUT => '///a'}],
+     ['m', qw(''),         {OUT => '.'}],
+    );
+
+# Append a newline to end of each expected `OUT' string.
+my $t;
+foreach $t (@Tests)
+  {
+    my $arg1 = $t->[1];
+    my $e;
+    foreach $e (@$t)
+      {
+       $e->{OUT} = "$e->{OUT}\n"
+         if ref $e eq 'HASH' and exists $e->{OUT};
+      }
+  }
+
+my $save_temps = $ENV{SAVE_TEMPS};
+my $verbose = $ENV{VERBOSE};
+
+my $fail = run_tests ($program_name, $prog, address@hidden, $save_temps, 
$verbose);
+exit $fail;
+EOF
Index: doc/coreutils.texi
===================================================================
RCS file: /cvsroot/coreutils/coreutils/doc/coreutils.texi,v
retrieving revision 1.296
diff -u -p -r1.296 coreutils.texi
--- doc/coreutils.texi  16 Nov 2005 22:45:12 -0000      1.296
+++ doc/coreutils.texi  23 Nov 2005 18:43:46 -0000
@@ -4636,7 +4636,7 @@ c
 $ paste num2 let3
 1       a
 2       b
-        c
+       @ c
 @end example
 
 Synopsis:
@@ -10394,8 +10394,21 @@ basename @var{name} address@hidden
 @end example
 
 If @var{suffix} is specified and is identical to the end of @var{name},
-it is removed from @var{name} as well.  @command{basename} prints the
-result on standard output.
+it is removed from @var{name} as well.  Note that since trailing slashes
+are removed prior to suffix matching, @var{suffix} will do nothing if it
+contains slashes.  @command{basename} prints the result on standard
+output.
+
+Together, @command{basename} and @command{dirname} are designed such
+that if @samp{ls "$name"} succeeds, then the command sequence @samp{cd
+"$(dirname "$name")"; ls "$(basename "$name")"} will too.  This works
+for everything except filenames containing a trailing newline.
+
address@hidden allows the implementation to define the results if
address@hidden is empty or @samp{//}.  In the former case, @acronym{GNU}
address@hidden returns the empty string.  In the latter case, the
+result is @samp{//} on platforms where @var{//} is distinct from
address@hidden/}, and @samp{/} on platforms where there is no difference.
 
 The only options are @option{--help} and @option{--version}.  @xref{Common
 options}.  Options must precede operands.
@@ -10430,6 +10443,16 @@ dirname @var{name}
 
 If @var{name} is a single component, @command{dirname} prints @samp{.}
 (meaning the current directory).
+
+Together, @command{basename} and @command{dirname} are designed such
+that if @samp{ls "$name"} succeeds, then the command sequence @samp{cd
+"$(dirname "$name")"; ls "$(basename "$name")"} will too.  This works
+for everything except filenames containing a trailing newline.
+
address@hidden allows the implementation to define the results if
address@hidden is @samp{//}.  With @acronym{GNU} @command{dirname}, the
+result is @samp{//} on platforms where @var{//} is distinct from
address@hidden/}, and @samp{/} on platforms where there is no difference.
 
 The only options are @option{--help} and @option{--version}.  @xref{Common
 options}.
Index: lib/backupfile.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/lib/backupfile.c,v
retrieving revision 1.49
diff -u -p -r1.49 backupfile.c
--- lib/backupfile.c    22 Sep 2005 06:05:39 -0000      1.49
+++ lib/backupfile.c    23 Nov 2005 18:43:48 -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/coreutils/coreutils/lib/basename.c,v
retrieving revision 1.26
diff -u -p -r1.26 basename.c
--- lib/basename.c      22 Sep 2005 06:05:39 -0000      1.26
+++ lib/basename.c      23 Nov 2005 18:43:48 -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] = '/';
+      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/coreutils/coreutils/lib/dirname.c,v
retrieving revision 1.36
diff -u -p -r1.36 dirname.c
--- lib/dirname.c       22 Sep 2005 06:05:39 -0000      1.36
+++ lib/dirname.c       23 Nov 2005 18:43:48 -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/coreutils/coreutils/lib/dirname.h,v
retrieving revision 1.15
diff -u -p -r1.15 dirname.h
--- lib/dirname.h       2 Jun 2005 05:05:29 -0000       1.15
+++ lib/dirname.h       23 Nov 2005 18:43:48 -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/coreutils/coreutils/lib/filenamecat.c,v
retrieving revision 1.2
diff -u -p -r1.2 filenamecat.c
--- lib/filenamecat.c   22 Sep 2005 06:05:39 -0000      1.2
+++ lib/filenamecat.c   23 Nov 2005 18:43:48 -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/coreutils/coreutils/lib/same.c,v
retrieving revision 1.18
diff -u -p -r1.18 same.c
--- lib/same.c  22 Sep 2005 06:05:39 -0000      1.18
+++ lib/same.c  23 Nov 2005 18:43:48 -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/coreutils/coreutils/lib/stripslash.c,v
retrieving revision 1.15
diff -u -p -r1.15 stripslash.c
--- lib/stripslash.c    22 Sep 2005 06:05:39 -0000      1.15
+++ lib/stripslash.c    23 Nov 2005 18:43:48 -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/coreutils/coreutils/m4/ChangeLog,v
retrieving revision 1.766
diff -u -p -r1.766 ChangeLog
Index: m4/dirname.m4
===================================================================
RCS file: /cvsroot/coreutils/coreutils/m4/dirname.m4,v
retrieving revision 1.7
diff -u -p -r1.7 dirname.m4
--- m4/dirname.m4       29 Jan 2005 00:16:39 -0000      1.7
+++ m4/dirname.m4       23 Nov 2005 18:43:49 -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/coreutils/coreutils/m4/dos.m4,v
retrieving revision 1.13
diff -u -p -r1.13 dos.m4
--- m4/dos.m4   23 Jan 2005 09:07:57 -0000      1.13
+++ m4/dos.m4   23 Nov 2005 18:43:49 -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: src/basename.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/basename.c,v
retrieving revision 1.63
diff -u -p -r1.63 basename.c
--- src/basename.c      2 Jun 2005 05:17:24 -0000       1.63
+++ src/basename.c      23 Nov 2005 18:43:49 -0000
@@ -126,12 +126,20 @@ main (int argc, char **argv)
     }
 
   name = base_name (argv[optind]);
-  name[base_len (name)] = '\0';
+  strip_trailing_slashes (name);
 
-  if (argc == optind + 2)
+  /* Per POSIX, `basename // /' must return `//' on platforms with
+     distinct //.  On platforms with drive letters, this generalizes
+     to making `basename c: :' return `c:'.  This rule is captured by
+     skipping suffix stripping if base_name returned an absolute path
+     or a drive letter (only possible if name is a file-system
+     root).  */
+  if (argc == optind + 2 && IS_RELATIVE_FILE_NAME (name)
+      && ! FILE_SYSTEM_PREFIX_LEN (name))
     remove_suffix (name, argv[optind + 1]);
 
   puts (name);
+  free (name);
 
   exit (EXIT_SUCCESS);
 }
Index: src/cp.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/cp.c,v
retrieving revision 1.215
diff -u -p -r1.215 cp.c
--- src/cp.c    16 Sep 2005 07:50:33 -0000      1.215
+++ src/cp.c    23 Nov 2005 18:43:49 -0000
@@ -1,5 +1,5 @@
 /* cp.c  -- file copying (main routines)
-   Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation.
+   Copyright (C) 1989, 1990, 1991, 1995-2005 Free Software Foundation.
 
    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
@@ -41,8 +41,8 @@
     {                                                  \
       char *tmp_abns_;                                 \
       ASSIGN_STRDUPA (tmp_abns_, (File_name));         \
+      Dest = last_component (tmp_abns_);               \
       strip_trailing_slashes (tmp_abns_);              \
-      Dest = base_name (tmp_abns_);                    \
     }                                                  \
   while (0)
 
@@ -175,7 +175,7 @@ Mandatory arguments to long options are 
 "), stdout);
       fputs (_("\
   -f, --force                  if an existing destination file cannot be\n\
-                                 opened, remove it and try again\n\
+                                opened, remove it and try again\n\
   -i, --interactive            prompt before overwrite\n\
   -H                           follow command-line symbolic links\n\
 "), stdout);
@@ -189,8 +189,8 @@ Mandatory arguments to long options are 
       fputs (_("\
   -p                           same as --preserve=mode,ownership,timestamps\n\
       --preserve[=ATTR_LIST]   preserve the specified attributes (default:\n\
-                                 mode,ownership,timestamps), if possible\n\
-                                 additional attributes: links, all\n\
+                                mode,ownership,timestamps), if possible\n\
+                                additional attributes: links, all\n\
 "), stdout);
       fputs (_("\
       --no-preserve=ATTR_LIST  don't preserve the specified attributes\n\
@@ -199,12 +199,12 @@ Mandatory arguments to long options are 
       fputs (_("\
   -R, -r, --recursive          copy directories recursively\n\
       --remove-destination     remove each existing destination file before\n\
-                                 attempting to open it (contrast with 
--force)\n\
+                                attempting to open it (contrast with 
--force)\n\
 "), stdout);
       fputs (_("\
       --sparse=WHEN            control creation of sparse files\n\
       --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
-                                 argument\n\
+                                argument\n\
 "), stdout);
       fputs (_("\
   -s, --symbolic-link          make symbolic links instead of copying\n\
@@ -214,8 +214,8 @@ Mandatory arguments to long options are 
 "), stdout);
       fputs (_("\
   -u, --update                 copy only when the SOURCE file is newer\n\
-                                 than the destination file or when the\n\
-                                 destination file is missing\n\
+                                than the destination file or when the\n\
+                                destination file is missing\n\
   -v, --verbose                explain what is being done\n\
   -x, --one-file-system        stay on this file system\n\
 "), stdout);
@@ -471,7 +471,7 @@ make_dir_parents_private (char const *co
 static bool
 target_directory_operand (char const *file, struct stat *st, bool *new_dst)
 {
-  char const *b = base_name (file);
+  char const *b = last_component (file);
   size_t blen = strlen (b);
   bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
   int err = (stat (file, st) == 0 ? 0 : errno);
@@ -581,8 +581,8 @@ do_copy (int n_files, char **file, const
                                           &arg_in_concat);
 
              /* For --parents, we have to make sure that the directory
-                dir_name (dst_name) exists.  We may have to create a few
-                leading directories. */
+                dir_name (dst_name) exists.  We may have to create a few
+                leading directories. */
              parent_exists =
                (make_dir_parents_private
                 (dst_name, arg_in_concat - dst_name, S_IRWXU,
Index: src/dircolors.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/dircolors.c,v
retrieving revision 1.97
diff -u -p -r1.97 dircolors.c
--- src/dircolors.c     24 Oct 2005 10:39:46 -0000      1.97
+++ src/dircolors.c     23 Nov 2005 18:43:49 -0000
@@ -99,7 +99,7 @@ usage (int status)
 {
   if (status != EXIT_SUCCESS)
     fprintf (stderr, _("Try `%s --help' for more information.\n"),
-             program_name);
+            program_name);
   else
     {
       printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
@@ -137,7 +137,7 @@ guess_shell_syntax (void)
   if (shell == NULL || *shell == '\0')
     return SHELL_SYNTAX_UNKNOWN;
 
-  shell = base_name (shell);
+  shell = last_component (shell);
 
   if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
     return SHELL_SYNTAX_C;
Index: src/install.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/install.c,v
retrieving revision 1.189
diff -u -p -r1.189 install.c
--- src/install.c       23 Sep 2005 20:50:49 -0000      1.189
+++ src/install.c       23 Nov 2005 18:43:50 -0000
@@ -168,7 +168,7 @@ cp_option_init (struct cp_options *x)
 static bool
 target_directory_operand (char const *file)
 {
-  char const *b = base_name (file);
+  char const *b = last_component (file);
   size_t blen = strlen (b);
   bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
   struct stat st;
@@ -372,8 +372,8 @@ main (int argc, char **argv)
       hash_init ();
 
       if (!target_directory)
-        {
-          if (mkdir_and_install)
+       {
+         if (mkdir_and_install)
            ok = install_file_in_file_parents (file[0], file[1], &x);
          else
            ok = install_file_in_file (file[0], file[1], &x);
@@ -464,7 +464,7 @@ static bool
 install_file_in_dir (const char *from, const char *to_dir,
                     const struct cp_options *x)
 {
-  const char *from_base = base_name (from);
+  const char *from_base = last_component (from);
   char *to = file_name_concat (to_dir, from_base, NULL);
   bool ret = install_file_in_file (from, to, x);
   free (to);
@@ -651,18 +651,18 @@ Mandatory arguments to long options are 
   -b                  like --backup but does not accept an argument\n\
   -c                  (ignored)\n\
   -d, --directory     treat all arguments as directory names; create all\n\
-                        components of the specified directories\n\
+                       components of the specified directories\n\
 "), stdout);
       fputs (_("\
   -D                  create all leading components of DEST except the last,\n\
-                        then copy SOURCE to DEST\n\
+                       then copy SOURCE to DEST\n\
   -g, --group=GROUP   set group ownership, instead of process' current group\n\
   -m, --mode=MODE     set permission mode (as in chmod), instead of 
rwxr-xr-x\n\
   -o, --owner=OWNER   set ownership (super-user only)\n\
 "), stdout);
       fputs (_("\
   -p, --preserve-timestamps   apply access/modification times of SOURCE 
files\n\
-                        to corresponding destination files\n\
+                       to corresponding destination files\n\
   -s, --strip         strip symbol tables\n\
   -S, --suffix=SUFFIX override the usual backup suffix\n\
   -t, --target-directory=DIRECTORY  copy all SOURCE arguments into DIRECTORY\n\
Index: src/ln.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/ln.c,v
retrieving revision 1.155
diff -u -p -r1.155 ln.c
--- src/ln.c    16 Nov 2005 22:32:45 -0000      1.155
+++ src/ln.c    23 Nov 2005 18:43:50 -0000
@@ -113,7 +113,7 @@ static struct option const long_options[
 static bool
 target_directory_operand (char const *file)
 {
-  char const *b = base_name (file);
+  char const *b = last_component (file);
   size_t blen = strlen (b);
   bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
   struct stat st;
@@ -339,20 +339,20 @@ Mandatory arguments to long options are 
       --backup[=CONTROL]      make a backup of each existing destination 
file\n\
   -b                          like --backup but does not accept an argument\n\
   -d, -F, --directory         allow the superuser to attempt to hard link\n\
-                                directories (note: will probably fail due to\n\
-                                system restrictions, even for the superuser)\n\
+                               directories (note: will probably fail due to\n\
+                               system restrictions, even for the superuser)\n\
   -f, --force                 remove existing destination files\n\
 "), stdout);
       fputs (_("\
   -n, --no-dereference        treat destination that is a symlink to a\n\
-                                directory as if it were a normal file\n\
+                               directory as if it were a normal file\n\
   -i, --interactive           prompt whether to remove destinations\n\
   -s, --symbolic              make symbolic links instead of hard links\n\
 "), stdout);
       fputs (_("\
   -S, --suffix=SUFFIX         override the usual backup suffix\n\
   -t, --target-directory=DIRECTORY  specify the DIRECTORY in which to create\n\
-                                the links\n\
+                               the links\n\
   -T, --no-target-directory   treat LINK_NAME as a normal file\n\
   -v, --verbose               print name of each linked file\n\
 "), stdout);
@@ -525,7 +525,8 @@ main (int argc, char **argv)
       for (i = 0; i < n_files; ++i)
        {
          char *dest_base;
-         char *dest = file_name_concat (target_directory, base_name (file[i]),
+         char *dest = file_name_concat (target_directory,
+                                        last_component (file[i]),
                                         &dest_base);
          strip_trailing_slashes (dest_base);
          ok &= do_link (file[i], dest);
Index: src/ls.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/ls.c,v
retrieving revision 1.403
diff -u -p -r1.403 ls.c
--- src/ls.c    17 Nov 2005 12:28:34 -0000      1.403
+++ src/ls.c    23 Nov 2005 18:43:51 -0000
@@ -1157,8 +1157,8 @@ main (int argc, char **argv)
        check_symlink_color = true;
 
       /* If the standard output is a controlling terminal, watch out
-         for signals, so that the colors can be restored to the
-         default state if "ls" is suspended or interrupted.  */
+        for signals, so that the colors can be restored to the
+        default state if "ls" is suspended or interrupted.  */
 
       if (0 <= tcgetpgrp (STDOUT_FILENO))
        {
@@ -1694,9 +1694,9 @@ decode_switches (int argc, char **argv)
            format = one_per_line;
          break;
 
-        case AUTHOR_OPTION:
-          print_author = true;
-          break;
+       case AUTHOR_OPTION:
+         print_author = true;
+         break;
 
        case HIDE_OPTION:
          {
@@ -2006,7 +2006,7 @@ get_funky_string (char **dest, const cha
              num = '\v';
              break;
            case '?':           /* Delete */
-              num = 127;
+             num = 127;
              break;
            case '_':           /* Space */
              num = ' ';
@@ -2617,8 +2617,8 @@ gobble_file (char const *name, enum file
              f->linkok = true;
 
              /* Symbolic links to directories that are mentioned on the
-                command line are automatically traced if not being
-                listed as files.  */
+                command line are automatically traced if not being
+                listed as files.  */
              if (!command_line_arg || format == long_format
                  || !S_ISDIR (linkstats.st_mode))
                {
@@ -2779,7 +2779,7 @@ make_link_name (char const *name, char c
 static bool
 basename_is_dot_or_dotdot (const char *name)
 {
-  char const *base = base_name (name);
+  char const *base = last_component (name);
   return DOT_OR_DOTDOT (base);
 }
 
@@ -4124,16 +4124,16 @@ Mandatory arguments to long options are 
       --block-size=SIZE      use SIZE-byte blocks\n\
   -B, --ignore-backups       do not list implied entries ending with ~\n\
   -c                         with -lt: sort by, and show, ctime (time of 
last\n\
-                               modification of file status information)\n\
-                               with -l: show ctime and sort by name\n\
-                               otherwise: sort by ctime\n\
+                              modification of file status information)\n\
+                              with -l: show ctime and sort by name\n\
+                              otherwise: sort by ctime\n\
 "), stdout);
       fputs (_("\
   -C                         list entries by columns\n\
       --color[=WHEN]         control whether color is used to distinguish 
file\n\
-                               types.  WHEN may be `never', `always', or 
`auto'\n\
+                              types.  WHEN may be `never', `always', or 
`auto'\n\
   -d, --directory            list directory entries instead of contents,\n\
-                               and do not dereference symbolic links\n\
+                              and do not dereference symbolic links\n\
   -D, --dired                generate output designed for Emacs' dired mode\n\
 "), stdout);
       fputs (_("\
@@ -4141,27 +4141,27 @@ Mandatory arguments to long options are 
   -F, --classify             append indicator (one of */=>@|) to entries\n\
       --file-type            likewise, except do not append `*'\n\
       --format=WORD          across -x, commas -m, horizontal -x, long -l,\n\
-                               single-column -1, verbose -l, vertical -C\n\
+                              single-column -1, verbose -l, vertical -C\n\
       --full-time            like -l --time-style=full-iso\n\
 "), stdout);
       fputs (_("\
   -g                         like -l, but do not list owner\n\
   -G, --no-group             like -l, but do not list group\n\
   -h, --human-readable       with -l, print sizes in human readable format\n\
-                               (e.g., 1K 234M 2G)\n\
+                              (e.g., 1K 234M 2G)\n\
       --si                   likewise, but use powers of 1000 not 1024\n\
   -H, --dereference-command-line\n\
-                             follow symbolic links listed on the command 
line\n\
+                            follow symbolic links listed on the command line\n\
       --dereference-command-line-symlink-to-dir\n\
-                             follow each command line symbolic link\n\
-                             that points to a directory\n\
+                            follow each command line symbolic link\n\
+                            that points to a directory\n\
       --hide=PATTERN         do not list implied entries matching shell 
PATTERN\n\
-                               (overridden by -a or -A)\n\
+                              (overridden by -a or -A)\n\
 "), stdout);
       fputs (_("\
       --indicator-style=WORD append indicator with style WORD to entry 
names:\n\
-                               none (default), slash (-p),\n\
-                               file-type (--file-type), classify (-F)\n\
+                              none (default), slash (-p),\n\
+                              file-type (--file-type), classify (-F)\n\
   -i, --inode                with -l, print the index number of each file\n\
   -I, --ignore=PATTERN       do not list implied entries matching shell 
PATTERN\n\
   -k                         like --block-size=1K\n\
@@ -4169,25 +4169,25 @@ Mandatory arguments to long options are 
       fputs (_("\
   -l                         use a long listing format\n\
   -L, --dereference          when showing file information for a symbolic\n\
-                               link, show information for the file the link\n\
-                               references rather than for the link itself\n\
+                              link, show information for the file the link\n\
+                              references rather than for the link itself\n\
   -m                         fill width with a comma separated list of 
entries\n\
 "), stdout);
       fputs (_("\
   -n, --numeric-uid-gid      like -l, but list numeric user and group IDs\n\
   -N, --literal              print raw entry names (don't treat e.g. control\n\
-                               characters specially)\n\
+                              characters specially)\n\
   -o                         like -l, but do not list group information\n\
   -p, --indicator-style=slash\n\
-                             append / indicator to directories\n\
+                            append / indicator to directories\n\
 "), stdout);
       fputs (_("\
   -q, --hide-control-chars   print ? instead of non graphic characters\n\
       --show-control-chars   show non graphic characters as-is (default\n\
-                             unless program is `ls' and output is a 
terminal)\n\
+                            unless program is `ls' and output is a terminal)\n\
   -Q, --quote-name           enclose entry names in double quotes\n\
       --quoting-style=WORD   use quoting style WORD for entry names:\n\
-                               literal, locale, shell, shell-always, c, 
escape\n\
+                              literal, locale, shell, shell-always, c, 
escape\n\
 "), stdout);
       fputs (_("\
   -r, --reverse              reverse order while sorting\n\
@@ -4197,27 +4197,27 @@ Mandatory arguments to long options are 
       fputs (_("\
   -S                         sort by file size\n\
       --sort=WORD            sort by WORD instead of name: none -U,\n\
-                             extension -X, size -S, time -t, version -v\n\
+                            extension -X, size -S, time -t, version -v\n\
       --time=WORD            with -l, show time as WORD instead of 
modification\n\
-                             time: atime -u, access -u, use -u, ctime -c,\n\
-                             or status -c; use specified time as sort key\n\
-                             if --sort=time\n\
+                            time: atime -u, access -u, use -u, ctime -c,\n\
+                            or status -c; use specified time as sort key\n\
+                            if --sort=time\n\
 "), stdout);
       fputs (_("\
       --time-style=STYLE     with -l, show times using style STYLE:\n\
-                             full-iso, long-iso, iso, locale, +FORMAT.\n\
-                             FORMAT is interpreted like `date'; if FORMAT is\n\
-                             FORMAT1<newline>FORMAT2, FORMAT1 applies to\n\
-                             non-recent files and FORMAT2 to recent files;\n\
-                             if STYLE is prefixed with `posix-', STYLE\n\
-                             takes effect only outside the POSIX locale\n\
+                            full-iso, long-iso, iso, locale, +FORMAT.\n\
+                            FORMAT is interpreted like `date'; if FORMAT is\n\
+                            FORMAT1<newline>FORMAT2, FORMAT1 applies to\n\
+                            non-recent files and FORMAT2 to recent files;\n\
+                            if STYLE is prefixed with `posix-', STYLE\n\
+                            takes effect only outside the POSIX locale\n\
   -t                         sort by modification time\n\
   -T, --tabsize=COLS         assume tab stops at each COLS instead of 8\n\
 "), stdout);
       fputs (_("\
   -u                         with -lt: sort by, and show, access time\n\
-                               with -l: show access time and sort by name\n\
-                               otherwise: sort by access time\n\
+                              with -l: show access time and sort by name\n\
+                              otherwise: sort by access time\n\
   -U                         do not sort; list entries in directory order\n\
   -v                         sort by version\n\
 "), stdout);
Index: src/mv.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/mv.c,v
retrieving revision 1.171
diff -u -p -r1.171 mv.c
--- src/mv.c    2 Nov 2005 21:52:33 -0000       1.171
+++ src/mv.c    23 Nov 2005 18:43:51 -0000
@@ -154,7 +154,7 @@ cp_option_init (struct cp_options *x)
 static bool
 target_directory_operand (char const *file)
 {
-  char const *b = base_name (file);
+  char const *b = last_component (file);
   size_t blen = strlen (b);
   bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
   struct stat st;
@@ -272,7 +272,7 @@ movefile (char *source, char *dest, bool
   if (dest_is_dir)
     {
       /* Treat DEST as a directory; build the full filename.  */
-      char const *src_basename = base_name (source);
+      char const *src_basename = last_component (source);
       char *new_dest = file_name_concat (dest, src_basename, NULL);
       strip_trailing_slashes (new_dest);
       ok = do_move (source, new_dest, x);
@@ -315,15 +315,15 @@ Mandatory arguments to long options are 
 "), stdout);
       fputs (_("\
       --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
-                                 argument\n\
+                                argument\n\
   -S, --suffix=SUFFIX          override the usual backup suffix\n\
 "), stdout);
       fputs (_("\
   -t, --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
   -T, --no-target-directory    treat DEST as a normal file\n\
   -u, --update                 move only when the SOURCE file is newer\n\
-                                 than the destination file or when the\n\
-                                 destination file is missing\n\
+                                than the destination file or when the\n\
+                                destination file is missing\n\
   -v, --verbose                explain what is being done\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
Index: src/remove.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/remove.c,v
retrieving revision 1.139
diff -u -p -r1.139 remove.c
--- src/remove.c        23 Nov 2005 04:52:48 -0000      1.139
+++ src/remove.c        23 Nov 2005 18:43:51 -0000
@@ -276,7 +276,7 @@ full_filename_ (Dirstack_state const *ds
   if (n_allocated < n_bytes_needed)
     {
       /* This code requires that realloc accept NULL as the first arg.
-         This function must not use xrealloc.  Otherwise, an out-of-memory
+        This function must not use xrealloc.  Otherwise, an out-of-memory
         error involving a file name to be expanded here wouldn't ever
         be issued.  Use realloc and fall back on using a static buffer
         if memory allocation fails.  */
@@ -1322,7 +1322,7 @@ static enum RM_status
 rm_1 (Dirstack_state *ds, char const *filename,
       struct rm_options const *x, bool *cwd_restore_failed)
 {
-  char const *base = base_name (filename);
+  char const *base = last_component (filename);
   if (DOT_OR_DOTDOT (base))
     {
       error (0, 0, _("cannot remove `.' or `..'"));
Index: src/rm.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/rm.c,v
retrieving revision 1.136
diff -u -p -r1.136 rm.c
--- src/rm.c    2 Nov 2005 21:53:20 -0000       1.136
+++ src/rm.c    23 Nov 2005 18:43:51 -0000
@@ -169,6 +169,7 @@ the contents of that file.  If you want 
 truly unrecoverable, consider using shred.\n\
 "), stdout);
       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+      free (base);
     }
   exit (status);
 }
Index: src/shred.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/shred.c,v
retrieving revision 1.117
diff -u -p -r1.117 shred.c
--- src/shred.c 24 Sep 2005 13:40:37 -0000      1.117
+++ src/shred.c 23 Nov 2005 18:43:51 -0000
@@ -170,7 +170,7 @@ Mandatory arguments to long options are 
   -u, --remove   truncate and remove file after overwriting\n\
   -v, --verbose  show progress\n\
   -x, --exact    do not round file sizes up to the next full block;\n\
-                   this is the default for non-regular files\n\
+                  this is the default for non-regular files\n\
   -z, --zero     add a final overwrite with zeros to hide shredding\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -258,7 +258,7 @@ struct isaac_state
 /* This index operation is more efficient on many processors */
 #define ind(mm, x) \
   (* (uint32_t *) ((char *) (mm) \
-                  + ((x) & (ISAAC_WORDS - 1) * sizeof (uint32_t))))
+                  + ((x) & (ISAAC_WORDS - 1) * sizeof (uint32_t))))
 
 /*
  * The central step.  This uses two temporaries, x and y.  mm is the
@@ -1344,7 +1344,7 @@ static bool
 wipename (char *oldname, char const *qoldname, struct Options const *flags)
 {
   char *newname = xstrdup (oldname);
-  char *base = base_name (newname);
+  char *base = last_component (newname);
   size_t len = base_len (base);
   char *dir = dir_name (newname);
   char *qdir = xstrdup (quotearg_colon (dir));
Index: src/split.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/split.c,v
retrieving revision 1.112
diff -u -p -r1.112 split.c
--- src/split.c 11 Jul 2005 18:24:42 -0000      1.112
+++ src/split.c 23 Nov 2005 18:43:51 -0000
@@ -126,7 +126,7 @@ Mandatory arguments to long options are 
 "), DEFAULT_SUFFIX_LENGTH);
       fputs (_("\
       --verbose           print a diagnostic to standard error just\n\
-                            before each output file is opened\n\
+                           before each output file is opened\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -171,7 +171,7 @@ next_file_name (void)
       {
        char *dir = dir_name (outfile);
        long name_max = pathconf (dir, _PC_NAME_MAX);
-       if (0 <= name_max && name_max < base_len (base_name (outfile)))
+       if (0 <= name_max && name_max < base_len (last_component (outfile)))
          error (EXIT_FAILURE, ENAMETOOLONG, "%s", outfile);
        free (dir);
       }
@@ -237,7 +237,7 @@ bytes_split (uintmax_t n_bytes, char *bu
     {
       n_read = full_read (STDIN_FILENO, buf, bufsize);
       if (n_read == SAFE_READ_ERROR)
-        error (EXIT_FAILURE, errno, "%s", infile);
+       error (EXIT_FAILURE, errno, "%s", infile);
       bp_out = buf;
       to_read = n_read;
       for (;;)
Index: src/su.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/su.c,v
retrieving revision 1.89
diff -u -p -r1.89 su.c
--- src/su.c    23 Aug 2005 15:09:13 -0000      1.89
+++ src/su.c    23 Nov 2005 18:43:51 -0000
@@ -219,7 +219,7 @@ log_su (struct passwd const *pw, bool su
   if (!tty)
     tty = "none";
   /* 4.2BSD openlog doesn't have the third parameter.  */
-  openlog (base_name (program_name), 0
+  openlog (last_component (program_name), 0
 # ifdef LOG_AUTH
           , LOG_AUTH
 # endif
@@ -282,7 +282,7 @@ modify_environment (const struct passwd 
   if (simulate_login)
     {
       /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
-         Unset all other environment variables.  */
+        Unset all other environment variables.  */
       char const *term = getenv ("TERM");
       if (term)
        term = xstrdup (term);
@@ -350,14 +350,14 @@ run_shell (char const *shell, char const
       char *arg0;
       char *shell_basename;
 
-      shell_basename = base_name (shell);
+      shell_basename = last_component (shell);
       arg0 = xmalloc (strlen (shell_basename) + 2);
       arg0[0] = '-';
       strcpy (arg0 + 1, shell_basename);
       args[0] = arg0;
     }
   else
-    args[0] = base_name (shell);
+    args[0] = last_component (shell);
   if (fast_startup)
     args[argno++] = "-f";
   if (command)
Index: tests/Makefile.am
===================================================================
RCS file: /cvsroot/coreutils/coreutils/tests/Makefile.am,v
retrieving revision 1.24
diff -u -p -r1.24 Makefile.am
--- tests/Makefile.am   4 Nov 2005 09:51:42 -0000       1.24
+++ tests/Makefile.am   23 Nov 2005 18:43:51 -0000
@@ -19,7 +19,7 @@ EXTRA_DIST = \
   rwx-to-mode sample-test setgid-check umask-check very-expensive
 
 SUBDIRS = \
-  basename chgrp chmod chown cp cut dd dircolors du expr factor \
+  basename chgrp chmod chown cp cut dd dircolors dirname du expr factor \
   fmt head install join ln ls ls-2 md5sum misc mkdir mv od pr readlink \
   rm rmdir seq sha1sum shred sort stty sum tac tail tail-2 tee test \
   touch tr tsort unexpand uniq wc
Index: tests/basename/basic
===================================================================
RCS file: /cvsroot/coreutils/coreutils/tests/basename/basic,v
retrieving revision 1.6
diff -u -p -r1.6 basic
--- tests/basename/basic        20 Apr 2005 07:54:54 -0000      1.6
+++ tests/basename/basic        23 Nov 2005 18:43:52 -0000
@@ -14,12 +14,18 @@ d=$srcdir/..
 exec $PERL -w -I$d -MCoreutils -- - << \EOF
 require 5.003;
 use strict;
+use File::stat;
 
 (my $program_name = $0) =~ s|.*/||;
 
 # Turn off localisation of executable's ouput.
 @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
 
+my $stat_single = stat('/');
+my $stat_double = stat('//');
+my $double_slash = ($stat_single->dev == $stat_double->dev
+                   && $stat_single->ino == $stat_double->ino) ? '/' : '//';
+
 my $prog = $ENV{PROG} || die "$0: \$PROG not specified in environment\n";
 
 my @Tests =
@@ -35,15 +41,21 @@ my @Tests =
      ['d', qw(d/f//),      {OUT => 'f'}],
      ['e', qw(f),          {OUT => 'f'}],
      ['f', qw(/),          {OUT => '/'}],
-     ['g', qw(//),         {OUT => '/'}],
+     ['g', qw(//),         {OUT => "$double_slash"}],
      ['h', qw(///),        {OUT => '/'}],
      ['i', qw(///a///),    {OUT => 'a'}],
+     ['j', qw(''),         {OUT => ''}],
      ['1', qw(f.s .s),     {OUT => 'f'}],
      ['2', qw(fs s),       {OUT => 'f'}],
      ['3', qw(fs fs),      {OUT => 'fs'}],
-     ['4', qw(fs fs),      {OUT => 'fs'}],
+     ['4', qw(fs/ s),      {OUT => 'f'}],
      ['5', qw(dir/file.suf .suf),      {OUT => 'file'}],
-    );
+     ['6', qw(// /),       {OUT => "$double_slash"}],
+     ['7', qw(// //),      {OUT => "$double_slash"}],
+     ['8', qw(fs x),       {OUT => 'fs'}],
+     ['9', qw(fs ''),      {OUT => 'fs'}],
+     ['10', qw(fs/ s/),    {OUT => 'fs'}],
+   );
 
 # Append a newline to end of each expected `OUT' string.
 my $t;
Index: tests/dirname/Makefile.am
===================================================================
RCS file: tests/dirname/Makefile.am
diff -N tests/dirname/Makefile.am
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/dirname/Makefile.am   23 Nov 2005 18:43:43 -0000
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in -*-Makefile-*-.
+AUTOMAKE_OPTIONS = 1.4 gnits
+
+TESTS = basic
+EXTRA_DIST = $(TESTS)
+TESTS_ENVIRONMENT = \
+  top_srcdir=$(top_srcdir) \
+  srcdir=$(srcdir) \
+  PERL="$(PERL)" \
+  PATH="`pwd`/../../src$(PATH_SEPARATOR)$$PATH" \
+  PROG=dirname
Index: tests/dirname/basic
===================================================================
RCS file: tests/dirname/basic
diff -N tests/dirname/basic
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/dirname/basic 23 Nov 2005 18:43:43 -0000
@@ -0,0 +1,71 @@
+#!/bin/sh
+# -*-perl-*-
+
+: ${PERL=perl}
+: ${srcdir=.}
+
+$PERL -e 1 > /dev/null 2>&1 || {
+  echo 1>&2 "$0: configure didn't find a usable version of Perl," \
+    "so can't run this test"
+  exit 77
+}
+
+d=$srcdir/..
+exec $PERL -w -I$d -MCoreutils -- - << \EOF
+require 5.003;
+use strict;
+use File::stat;
+
+(my $program_name = $0) =~ s|.*/||;
+
+# Turn off localisation of executable's ouput.
address@hidden(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+my $stat_single = stat('/');
+my $stat_double = stat('//');
+my $double_slash = ($stat_single->dev == $stat_double->dev
+                   && $stat_single->ino == $stat_double->ino) ? '/' : '//';
+
+my $prog = $ENV{PROG} || die "$0: \$PROG not specified in environment\n";
+
+my @Tests =
+    (
+     ['fail-1', {ERR => "$prog: missing operand\n"
+       . "Try `$prog --help' for more information.\n"}, {EXIT => '1'}],
+     ['fail-2', qw(a b), {ERR => "$prog: extra operand `b'\n"
+       . "Try `$prog --help' for more information.\n"}, {EXIT => '1'}],
+
+     ['a', qw(d/f),        {OUT => 'd'}],
+     ['b', qw(/d/f),       {OUT => '/d'}],
+     ['c', qw(d/f/),       {OUT => 'd'}],
+     ['d', qw(d/f//),      {OUT => 'd'}],
+     ['e', qw(f),          {OUT => '.'}],
+     ['f', qw(/),          {OUT => '/'}],
+     ['g', qw(//),         {OUT => "$double_slash"}],
+     ['h', qw(///),        {OUT => '/'}],
+     ['i', qw(//a//),      {OUT => "$double_slash"}],
+     ['j', qw(///a///),    {OUT => '/'}],
+     ['k', qw(///a///b),   {OUT => '///a'}],
+     ['l', qw(///a//b/),   {OUT => '///a'}],
+     ['m', qw(''),         {OUT => '.'}],
+    );
+
+# Append a newline to end of each expected `OUT' string.
+my $t;
+foreach $t (@Tests)
+  {
+    my $arg1 = $t->[1];
+    my $e;
+    foreach $e (@$t)
+      {
+       $e->{OUT} = "$e->{OUT}\n"
+         if ref $e eq 'HASH' and exists $e->{OUT};
+      }
+  }
+
+my $save_temps = $ENV{SAVE_TEMPS};
+my $verbose = $ENV{VERBOSE};
+
+my $fail = run_tests ($program_name, $prog, address@hidden, $save_temps, 
$verbose);
+exit $fail;
+EOF

reply via email to

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