#include "dirname.h"
+#ifndef FILESYSTEM_PREFIX_LEN
+# define FILESYSTEM_PREFIX_LEN(Filename) 0
+#endif
+
#ifndef ISSLASH
# define ISSLASH(C) ((C) == '/')
#endif
#define BACKSLASH_IS_PATH_SEPARATOR ISSLASH ('\\')
-/* Return the length of the directory part of PATH.
- Set *RESULT to point to PATH or to `"."', as appropriate. */
-size_t
-dir_name_r (const char *path, const char **result)
+/* Return the length of `dirname (PATH)' and set *RESULT to point
+ to PATH or to `"."', as appropriate. Works properly even if
+ there are trailing slashes (by effectively ignoring them).
+ WARNING: This function doesn't work for cwd-relative names like
+ `a:foo' that are specified with a drive-letter prefix. That case
+ is handled in the caller. */
+static size_t
+dir_name_r (char const *path, char const **result)
{
- char *slash;
- int length; /* Length of result, not including NUL. */
+ char const *slash;
+ size_t length; /* Length of result, not including NUL. */
slash = strrchr (path, '/');
if (BACKSLASH_IS_PATH_SEPARATOR)
{
- char *b = strrchr (path, '\\');
+ char const *b = strrchr (path, '\\');
if (b && slash < b)
slash = b;
}
if (path < slash)
{
- slash = memrchr (path, '/', slash - path);
+ size_t len = slash - path;
+ slash = memrchr (path, '/', len);
if (BACKSLASH_IS_PATH_SEPARATOR)
{
- char *b = memrchr (path, '\\', slash - path);
+ char const *b = memrchr (path, '\\', len);
if (b && slash < b)
slash = b;
}
if (slash == 0)
{
/* File is in the current directory. */
- path = ".";
- length = 1;
+
+ length = FILESYSTEM_PREFIX_LEN (path);
+
+ if (length == 0)
+ {
+ path = ".";
+ length = 1;
+ }
}
else
{
- /* Remove any trailing slashes from the result. */
- if (BACKSLASH_IS_PATH_SEPARATOR)
- {
- const char *lim = ((path[0] >= 'A' && path[0] <= 'z'
- && path[1] == ':')
- ? path + 2 : path);
+ /* Remove any trailing slashes from the result. If we have a
+ canonicalized "d:/path", leave alone the root case "d:/". */
+ char const *lim = path + FILESYSTEM_PREFIX_LEN (path);
- /* If canonicalized "d:/path", leave alone the root case "d:/". */
- while (slash > lim && ISSLASH (*slash))
- --slash;
- }
- else
- {
- while (slash > path && ISSLASH (*slash))
- --slash;
- }
+ while (lim < slash && ISSLASH (*slash))
+ --slash;
length = slash - path + 1;
}
(by effectively ignoring them). */
char *
-dir_name (const char *path)
+dir_name (char const *path)
{
- const char *result;
+ char const *result;
size_t length = dir_name_r (path, &result);
- char *newpath = (char *) malloc (length + 1);
+ int append_dot = (length && length == FILESYSTEM_PREFIX_LEN (newpath));
+ char *newpath = (char *) malloc (length + append_dot + 1);
if (newpath == 0)
return 0;
strncpy (newpath, result, length);
+ /* If PATH is "d:foo", return "d:.", the CWD on drive d: */
+ if (append_dot)
+ newpath[length++] = '.';
newpath[length] = 0;
return newpath;
}
{
char path[MAX_BUFF_LEN];
char expected_result[MAX_BUFF_LEN];
- char *result;
+ char const *result;
sscanf (buff, "%s %s", path, expected_result);
result = dir_name (path);
if (strcmp (result, expected_result))