Rework Win32 basename and dirname implementation
Don't use _splitpath_s and _makepath_s as they don't support long paths and UNC paths properly. Report error if path is not valid.
This commit is contained in:
parent
650db1f46e
commit
5f420cafda
|
@ -621,69 +621,117 @@ test_path_resolve (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct xname_test_data
|
||||
{
|
||||
const char * input;
|
||||
const char * output;
|
||||
};
|
||||
|
||||
static int
|
||||
test_path_xname (const struct xname_test_data * data,
|
||||
size_t data_size,
|
||||
char * (* func) (const char *, tr_error **))
|
||||
{
|
||||
for (size_t i = 0; i < data_size; ++i)
|
||||
{
|
||||
tr_error * err = NULL;
|
||||
char * name = func (data[i].input, &err);
|
||||
|
||||
if (data[i].output != NULL)
|
||||
{
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (data[i].output, name);
|
||||
tr_free (name);
|
||||
}
|
||||
else
|
||||
{
|
||||
check (name == NULL);
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_basename_dirname (void)
|
||||
{
|
||||
tr_error * err = NULL;
|
||||
char * name;
|
||||
|
||||
name = tr_sys_path_basename ("/a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("c", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_basename ("", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("/a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("/a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
const struct xname_test_data basename_tests[] =
|
||||
{
|
||||
{ "a", "a" },
|
||||
{ "aa", "aa" },
|
||||
{ "/aa", "aa" },
|
||||
{ "/a/b/c", "c" },
|
||||
{ "/a/b/c/", "c" },
|
||||
{ "/", "/" },
|
||||
{ "", "." },
|
||||
#ifdef _WIN32
|
||||
|
||||
name = tr_sys_path_basename ("c:\\a\\b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("c", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("C:\\a/b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("C:\\a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a/b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
{ "c:\\a\\b\\c", "c" },
|
||||
{ "c:", "/" },
|
||||
{ "c:/", "/" },
|
||||
{ "c:\\", "/" },
|
||||
{ "c:a/b", "b" },
|
||||
{ "c:a", "a" },
|
||||
{ "\\\\a\\b\\c", "c" },
|
||||
{ "//a/b", "b" },
|
||||
{ "//1.2.3.4/b", "b" },
|
||||
{ "\\\\a", "a" },
|
||||
{ "\\\\1.2.3.4", "1.2.3.4" },
|
||||
{ "\\\\", "/" },
|
||||
{ "\\", "/" },
|
||||
{ "\\a", "a" },
|
||||
{ "\\\\\\", NULL },
|
||||
{ "123:" , NULL }
|
||||
#else
|
||||
{ "////", "/" }
|
||||
#endif
|
||||
};
|
||||
|
||||
if (test_path_xname (basename_tests, sizeof (basename_tests) / sizeof (*basename_tests), tr_sys_path_basename) != 0)
|
||||
return 1;
|
||||
|
||||
const struct xname_test_data dirname_tests[] =
|
||||
{
|
||||
{ "/a/b/c", "/a/b" },
|
||||
{ "a/b/c", "a/b" },
|
||||
{ "a/b/c/", "a/b" },
|
||||
{ "a", "." },
|
||||
{ "a/", "." },
|
||||
{ "/", "/" },
|
||||
{ "", "." },
|
||||
#ifdef _WIN32
|
||||
{ "C:\\a/b\\c", "C:\\a/b" },
|
||||
{ "C:\\a/b\\c\\", "C:\\a/b" },
|
||||
{ "C:\\a/b", "C:\\a" },
|
||||
{ "C:/a", "C:" },
|
||||
{ "C:", "C:" },
|
||||
{ "C:/", "C:" },
|
||||
{ "C:\\", "C:" },
|
||||
{ "c:a/b", "c:a" },
|
||||
{ "c:a", "c:." },
|
||||
{ "c:.", "c:." },
|
||||
{ "\\\\a\\b\\c", "\\\\a\\b" },
|
||||
{ "\\\\a\\b\\c/", "\\\\a\\b" },
|
||||
{ "//a/b", "//a" },
|
||||
{ "//1.2.3.4/b", "//1.2.3.4" },
|
||||
{ "\\\\a", "\\\\" },
|
||||
{ "\\\\1.2.3.4", "\\\\" },
|
||||
{ "\\\\", "\\\\" },
|
||||
{ "\\", "/" },
|
||||
{ "a/b\\c", "a/b" },
|
||||
{ "\\\\\\" , NULL },
|
||||
{ "123:" , NULL }
|
||||
#else
|
||||
{ "////", "/" }
|
||||
#endif
|
||||
};
|
||||
|
||||
if (test_path_xname (dirname_tests, sizeof (dirname_tests) / sizeof (*dirname_tests), tr_sys_path_dirname) != 0)
|
||||
return 1;
|
||||
|
||||
/* TODO: is_same (dirname (x) + '/' + basename (x), x) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <ctype.h> /* isalpha () */
|
||||
#include <stdlib.h> /* _splitpath_s (), _makepath_s () */
|
||||
|
||||
#include <shlobj.h> /* SHCreateDirectoryEx () */
|
||||
#include <winioctl.h> /* FSCTL_SET_SPARSE */
|
||||
|
@ -108,6 +107,40 @@ stat_to_sys_path_info (DWORD attributes,
|
|||
info->last_modified_at = filetime_to_unix_time (mtime);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_slash (char c)
|
||||
{
|
||||
return c == '\\' || c == '/';
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_unc_path (const char * path)
|
||||
{
|
||||
return is_slash (path[0]) && path[1] == path[0];
|
||||
}
|
||||
|
||||
static bool
|
||||
is_valid_path (const char * path)
|
||||
{
|
||||
if (is_unc_path (path))
|
||||
{
|
||||
if (path[2] != '\0' && !isalnum (path[2]))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * colon_pos = strchr (path, ':');
|
||||
if (colon_pos != NULL)
|
||||
{
|
||||
if (colon_pos != path + 1 || !isalpha (path[0]))
|
||||
return false;
|
||||
path += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return strpbrk (path, "<>:\"|?*") == NULL;
|
||||
}
|
||||
|
||||
static tr_sys_file_t
|
||||
open_file (const char * path,
|
||||
DWORD access,
|
||||
|
@ -454,57 +487,70 @@ char *
|
|||
tr_sys_path_basename (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char fname[_MAX_FNAME], ext[_MAX_EXT];
|
||||
if (path == NULL || path[0] == '\0')
|
||||
return tr_strdup (".");
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
(void) error;
|
||||
|
||||
/* TODO: Error handling */
|
||||
|
||||
if (_splitpath_s (path, NULL, 0, NULL, 0, fname, sizeof (fname), ext, sizeof (ext)) == 0 &&
|
||||
(*fname != '\0' || *ext != '\0'))
|
||||
if (!is_valid_path (path))
|
||||
{
|
||||
const size_t tmp_len = strlen (fname) + strlen (ext) + 2;
|
||||
char * const tmp = tr_new (char, tmp_len);
|
||||
if (_makepath_s (tmp, tmp_len, NULL, NULL, fname, ext) == 0)
|
||||
return tmp;
|
||||
tr_free (tmp);
|
||||
set_system_error (error, ERROR_PATH_NOT_FOUND);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tr_strdup (".");
|
||||
const char * end = path + strlen (path);
|
||||
while (end > path && is_slash (*(end - 1)))
|
||||
--end;
|
||||
|
||||
if (end == path)
|
||||
return tr_strdup ("/");
|
||||
|
||||
const char * name = end;
|
||||
while (name > path && *(name - 1) != ':' && !is_slash (*(name - 1)))
|
||||
--name;
|
||||
|
||||
if (name == end)
|
||||
return tr_strdup ("/");
|
||||
|
||||
return tr_strndup (name, end - name);
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_dirname (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char drive[_MAX_DRIVE], dir[_MAX_DIR];
|
||||
if (path == NULL || path[0] == '\0')
|
||||
return tr_strdup (".");
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
(void) error;
|
||||
|
||||
/* TODO: Error handling */
|
||||
|
||||
if (_splitpath_s (path, drive, sizeof (drive), dir, sizeof (dir), NULL, 0, NULL, 0) == 0 &&
|
||||
(*drive != '\0' || *dir != '\0'))
|
||||
if (!is_valid_path (path))
|
||||
{
|
||||
const size_t tmp_len = strlen (drive) + strlen (dir) + 2;
|
||||
char * const tmp = tr_new (char, tmp_len);
|
||||
if (_makepath_s (tmp, tmp_len, drive, dir, NULL, NULL) == 0)
|
||||
{
|
||||
size_t len = strlen(tmp);
|
||||
while (len > 0 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\'))
|
||||
tmp[--len] = '\0';
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
tr_free (tmp);
|
||||
set_system_error (error, ERROR_PATH_NOT_FOUND);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tr_strdup (".");
|
||||
const bool is_unc = is_unc_path (path);
|
||||
|
||||
if (is_unc && path[2] == '\0')
|
||||
return tr_strdup (path);
|
||||
|
||||
const char * end = path + strlen (path);
|
||||
while (end > path && is_slash (*(end - 1)))
|
||||
--end;
|
||||
|
||||
if (end == path)
|
||||
return tr_strdup ("/");
|
||||
|
||||
const char * name = end;
|
||||
while (name > path && *(name - 1) != ':' && !is_slash (*(name - 1)))
|
||||
--name;
|
||||
while (name > path && is_slash (*(name - 1)))
|
||||
--name;
|
||||
|
||||
if (name == path)
|
||||
return tr_strdup (is_unc ? "\\\\" : ".");
|
||||
|
||||
if (name > path && *(name - 1) == ':' && *name != '\0' && !is_slash (*name))
|
||||
return tr_strdup_printf ("%c:.", path[0]);
|
||||
|
||||
return tr_strndup (path, name - path);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
Loading…
Reference in New Issue