summaryrefslogtreecommitdiff
path: root/lib/dirname.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2006-03-26 12:20:24 +0000
committerJim Meyering <jim@meyering.net>2006-03-26 12:20:24 +0000
commit8132c6e76ee97bc1af375ff1701729e0ac5218b9 (patch)
tree94506c92ac347f7b97a1242e95d67ad9d90039af /lib/dirname.c
parentba0f1e7d7f3b63428834eb32d37da2cc8fef1944 (diff)
downloadcoreutils-8132c6e76ee97bc1af375ff1701729e0ac5218b9.tar.xz
(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.
Diffstat (limited to 'lib/dirname.c')
-rw-r--r--lib/dirname.c112
1 files changed, 39 insertions, 73 deletions
diff --git a/lib/dirname.c b/lib/dirname.c
index e2b9d648c..e3c762c7d 100644
--- a/lib/dirname.c
+++ b/lib/dirname.c
@@ -1,6 +1,6 @@
/* dirname.c -- return all but the last element in a file name
- Copyright (C) 1990, 1998, 2000, 2001, 2003, 2004, 2005 Free Software
+ Copyright (C) 1990, 1998, 2000, 2001, 2003, 2004, 2005, 2006 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
@@ -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