Basic support for UNC paths on Windows
Differential Revision: https://developer.blender.org/D298 Allows users on Windows to enter UNC paths in the filebrowser and to link to .blend files on a UNC path. Functionality is limited still, we can't browse the network yet and have no support to check user rights so far. What works: - enter an UNC path in the file browser manually or via copy/paste - navigation within the UNC share subfolders - link to a file on a UNC share What does not (yet) work: - browse the network for computers and shares - browse to a folder that requires entering user credentials Contributors: Rob McKay - original patch Campbell Barton - style fixes Reviewers: Campbell Barton, Brecht van Lommel
This commit is contained in:
Submodule release/datafiles/locale updated: c842f10f94...cb1967cc63
Submodule release/scripts/addons updated: 8ad356e332...c50944e808
Submodule release/scripts/addons_contrib updated: 7485bc6b11...31545d25c9
2
scons
2
scons
Submodule scons updated: 59512aadd1...2d6ebcb239
@@ -165,6 +165,12 @@ bool BLI_path_cwd(char *path) ATTR_NONNULL();
|
||||
void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL();
|
||||
|
||||
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
|
||||
bool BLI_path_is_unc(const char *path);
|
||||
|
||||
#if defined(WIN32)
|
||||
void BLI_cleanup_unc_16(wchar_t *path_16);
|
||||
void BLI_cleanup_unc(char *path_16, int maxlen);
|
||||
#endif
|
||||
|
||||
/* path string comparisons: case-insensitive for Windows, case-sensitive otherwise */
|
||||
#if defined(WIN32)
|
||||
|
@@ -329,7 +329,7 @@ void BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim
|
||||
BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len);
|
||||
}
|
||||
|
||||
|
||||
static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
|
||||
|
||||
/* ******************** string encoding ***************** */
|
||||
|
||||
@@ -394,7 +394,9 @@ void BLI_cleanup_path(const char *relabase, char *path)
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
}
|
||||
|
||||
while ( (start = strstr(path, "\\\\")) ) {
|
||||
/* remove two consecutive backslashes, but skip the UNC prefix,
|
||||
* which needs to be preserved */
|
||||
while ( (start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\")) ) {
|
||||
eind = start + strlen("\\\\") - 1;
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
}
|
||||
@@ -463,6 +465,110 @@ bool BLI_path_is_rel(const char *path)
|
||||
return path[0] == '/' && path[1] == '/';
|
||||
}
|
||||
|
||||
/* return true if the path is a UNC share */
|
||||
bool BLI_path_is_unc(const char *name)
|
||||
{
|
||||
return name[0] == '\\' && name[1] == '\\';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the identifying prefix
|
||||
* of a UNC path which can start with '\\' (short version)
|
||||
* or '\\?\' (long version)
|
||||
* If the path is not a UNC path, return 0
|
||||
*
|
||||
* \param name the path name
|
||||
*/
|
||||
static int BLI_path_unc_prefix_len(const char *path)
|
||||
{
|
||||
if (BLI_path_is_unc(path)) {
|
||||
if ((path[2] == '?') && (path[3] == '\\') ) {
|
||||
/* we assume long UNC path like \\?\server\share\folder etc... */
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
/* return true if the path is absolute ie starts with a drive specifier (eg A:\) or is a UNC path */
|
||||
static bool BLI_path_is_abs(const char *name)
|
||||
{
|
||||
return (name[1] == ':' && (name[2] == '\\' || name[2] == '/') ) || BLI_path_is_unc(name);
|
||||
}
|
||||
|
||||
static wchar_t *next_slash(wchar_t *path)
|
||||
{
|
||||
wchar_t *slash = path;
|
||||
while (*slash && *slash != L'\\') slash++;
|
||||
return slash;
|
||||
}
|
||||
|
||||
/* adds a slash if the unc path points sto a share */
|
||||
static void BLI_path_add_slash_to_share(wchar_t *uncpath)
|
||||
{
|
||||
wchar_t *slash_after_server = next_slash(uncpath + 2);
|
||||
if (*slash_after_server) {
|
||||
wchar_t *slash_after_share = next_slash(slash_after_server + 1);
|
||||
if (!(*slash_after_share)) {
|
||||
slash_after_share[0] = L'\\';
|
||||
slash_after_share[1] = L'\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BLI_path_unc_to_short(wchar_t *unc)
|
||||
{
|
||||
wchar_t tmp[PATH_MAX];
|
||||
|
||||
int len = wcslen(unc);
|
||||
int copy_start = 0;
|
||||
/* convert:
|
||||
* \\?\UNC\server\share\folder\... to \\server\share\folder\...
|
||||
* \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
|
||||
*/
|
||||
if ((len > 3) &&
|
||||
(unc[0] == L'\\') &&
|
||||
(unc[1] == L'\\') &&
|
||||
(unc[2] == L'?') &&
|
||||
((unc[3] == L'\\') || (unc[3] == L'/')))
|
||||
{
|
||||
if ((len > 5) && (unc[5] == L':')) {
|
||||
wcsncpy(tmp, unc + 4, len - 4);
|
||||
tmp[len - 4] = L'\0';
|
||||
wcscpy(unc, tmp);
|
||||
}
|
||||
else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) &&
|
||||
((unc[7] == L'\\') || (unc[7] == L'/')))
|
||||
{
|
||||
tmp[0] = L'\\';
|
||||
tmp[1] = L'\\';
|
||||
wcsncpy(tmp + 2, unc + 8, len - 8);
|
||||
tmp[len - 6] = L'\0';
|
||||
wcscpy(unc, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_cleanup_unc(char *path, int maxlen)
|
||||
{
|
||||
wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
|
||||
BLI_cleanup_unc_16(tmp_16);
|
||||
conv_utf_16_to_8(tmp_16, path, maxlen);
|
||||
}
|
||||
|
||||
void BLI_cleanup_unc_16(wchar_t *path_16)
|
||||
{
|
||||
BLI_path_unc_to_short(path_16);
|
||||
BLI_path_add_slash_to_share(path_16);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given
|
||||
* the same *relfile, will convert it back to its original value.
|
||||
@@ -484,7 +590,7 @@ void BLI_path_rel(char *file, const char *relfile)
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (BLI_strnlen(relfile, 3) > 2 && relfile[1] != ':') {
|
||||
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
|
||||
char *ptemp;
|
||||
/* fix missing volume name in relative base,
|
||||
* can happen with old recent-files.txt files */
|
||||
@@ -500,15 +606,35 @@ void BLI_path_rel(char *file, const char *relfile)
|
||||
}
|
||||
|
||||
if (BLI_strnlen(file, 3) > 2) {
|
||||
if (temp[1] == ':' && file[1] == ':' && temp[0] != file[0])
|
||||
bool is_unc = BLI_path_is_unc(file);
|
||||
|
||||
/* Ensure paths are both UNC paths or are both drives */
|
||||
if (BLI_path_is_unc(temp) != is_unc) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure both UNC paths are on the same share */
|
||||
if (is_unc) {
|
||||
int off;
|
||||
int slash = 0;
|
||||
for (off = 0; temp[off] && slash < 4; off++) {
|
||||
if (temp[off] != file[off])
|
||||
return;
|
||||
|
||||
if (temp[off] == '\\')
|
||||
slash++;
|
||||
}
|
||||
}
|
||||
else if (temp[1] == ':' && file[1] == ':' && temp[0] != file[0]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
BLI_strncpy(temp, relfile, FILE_MAX);
|
||||
#endif
|
||||
|
||||
BLI_char_switch(temp, '\\', '/');
|
||||
BLI_char_switch(file, '\\', '/');
|
||||
BLI_char_switch(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
|
||||
BLI_char_switch(file + BLI_path_unc_prefix_len(file), '\\', '/');
|
||||
|
||||
/* remove /./ which confuse the following slash counting... */
|
||||
BLI_cleanup_path(NULL, file);
|
||||
@@ -727,7 +853,7 @@ bool BLI_path_abs(char *path, const char *basepath)
|
||||
* blend file as a lib main - we are basically checking for the case that a
|
||||
* UNIX root '/' is passed.
|
||||
*/
|
||||
if (!wasrelative && (vol[1] != ':' && (vol[0] == '\0' || vol[0] == '/' || vol[0] == '\\'))) {
|
||||
if (!wasrelative && !BLI_path_is_abs(path)) {
|
||||
char *p = path;
|
||||
get_default_root(tmp);
|
||||
// get rid of the slashes at the beginning of the path
|
||||
@@ -757,24 +883,28 @@ bool BLI_path_abs(char *path, const char *basepath)
|
||||
|
||||
#endif
|
||||
|
||||
BLI_strncpy(base, basepath, sizeof(base));
|
||||
|
||||
/* file component is ignored, so don't bother with the trailing slash */
|
||||
BLI_cleanup_path(NULL, base);
|
||||
|
||||
/* push slashes into unix mode - strings entering this part are
|
||||
* potentially messed up: having both back- and forward slashes.
|
||||
* Here we push into one conform direction, and at the end we
|
||||
* push them into the system specific dir. This ensures uniformity
|
||||
* of paths and solving some problems (and prevent potential future
|
||||
* ones) -jesterKing. */
|
||||
BLI_char_switch(tmp, '\\', '/');
|
||||
BLI_char_switch(base, '\\', '/');
|
||||
* ones) -jesterKing.
|
||||
* For UNC paths the first characters containing the UNC prefix
|
||||
* shouldn't be switched as we need to distinguish them from
|
||||
* paths relative to the .blend file -elubie */
|
||||
BLI_char_switch(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
|
||||
|
||||
/* Paths starting with // will get the blend file as their base,
|
||||
* this isn't standard in any os but is used in blender all over the place */
|
||||
if (wasrelative) {
|
||||
const char * const lslash = BLI_last_slash(base);
|
||||
const char *lslash;
|
||||
BLI_strncpy(base, basepath, sizeof(base));
|
||||
|
||||
/* file component is ignored, so don't bother with the trailing slash */
|
||||
BLI_cleanup_path(NULL, base);
|
||||
lslash = BLI_last_slash(base);
|
||||
BLI_char_switch(base + BLI_path_unc_prefix_len(base), '\\', '/');
|
||||
|
||||
if (lslash) {
|
||||
const int baselen = (int) (lslash - base) + 1; /* length up to and including last "/" */
|
||||
/* use path for temp storage here, we copy back over it right away */
|
||||
@@ -823,7 +953,7 @@ bool BLI_path_cwd(char *path)
|
||||
const int filelen = strlen(path);
|
||||
|
||||
#ifdef WIN32
|
||||
if (filelen >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
|
||||
if ((filelen >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path))
|
||||
wasrelative = false;
|
||||
#else
|
||||
if (filelen >= 2 && path[0] == '/')
|
||||
@@ -1369,7 +1499,7 @@ void BLI_clean(char *path)
|
||||
BLI_char_switch(path + 2, '/', '\\');
|
||||
}
|
||||
#else
|
||||
BLI_char_switch(path, '\\', '/');
|
||||
BLI_char_switch(path + BLI_path_unc_prefix_len(path), '\\', '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1486,9 +1616,12 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c
|
||||
BLI_strncpy(string, dir, 3);
|
||||
dir += 2;
|
||||
}
|
||||
else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
|
||||
string[0] = 0;
|
||||
}
|
||||
else { /* no drive specified */
|
||||
/* first option: get the drive from the relabase if it has one */
|
||||
if (relabase && strlen(relabase) >= 2 && relabase[1] == ':') {
|
||||
if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
|
||||
BLI_strncpy(string, relabase, 3);
|
||||
string[2] = '\\';
|
||||
string[3] = '\0';
|
||||
|
@@ -478,16 +478,29 @@ int BLI_exists(const char *name)
|
||||
#else
|
||||
struct _stati64 st;
|
||||
#endif
|
||||
/* in Windows stat doesn't recognize dir ending on a slash
|
||||
* To not break code where the ending slash is expected we
|
||||
* don't mess with the argument name directly here - elubie */
|
||||
wchar_t *tmp_16 = alloc_utf16_from_8(name, 0);
|
||||
wchar_t *tmp_16 = alloc_utf16_from_8(name, 1);
|
||||
int len, res;
|
||||
unsigned int old_error_mode;
|
||||
|
||||
len = wcslen(tmp_16);
|
||||
if (len > 3 && (tmp_16[len - 1] == L'\\' || tmp_16[len - 1] == L'/'))
|
||||
/* in Windows #stat doesn't recognize dir ending on a slash
|
||||
* so we remove it here */
|
||||
if (len > 3 && (tmp_16[len - 1] == L'\\' || tmp_16[len - 1] == L'/')) {
|
||||
tmp_16[len - 1] = '\0';
|
||||
}
|
||||
/* two special cases where the trailing slash is needed:
|
||||
* 1. after the share part of a UNC path
|
||||
* 2. after the C:\ when the path is the volume only
|
||||
*/
|
||||
if ((len >= 3) && (tmp_16[0] == L'\\') && (tmp_16[1] == L'\\')) {
|
||||
BLI_cleanup_unc_16(tmp_16);
|
||||
}
|
||||
|
||||
if ((tmp_16[1] == L':') && (tmp_16[2] == L'\0')) {
|
||||
tmp_16[2] = L'\\';
|
||||
tmp_16[3] = L'\0';
|
||||
}
|
||||
|
||||
|
||||
/* change error mode so user does not get a "no disk in drive" popup
|
||||
* when looking for a file on an empty CD/DVD drive */
|
||||
|
@@ -857,7 +857,19 @@ int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
|
||||
if (sfile->params) {
|
||||
if (BLI_parent_dir(sfile->params->dir)) {
|
||||
BLI_cleanup_dir(G.main->name, sfile->params->dir);
|
||||
file_change_dir(C, 0);
|
||||
/* if not browsing in .blend file, we still want to check whether the path is a directory */
|
||||
if (sfile->params->type == FILE_LOADLIB) {
|
||||
char tdir[FILE_MAX], tgroup[FILE_MAX];
|
||||
if (BLO_is_a_library(sfile->params->dir, tdir, tgroup)) {
|
||||
file_change_dir(C, 0);
|
||||
}
|
||||
else {
|
||||
file_change_dir(C, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
file_change_dir(C, 1);
|
||||
}
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1206,10 +1218,30 @@ static void file_expand_directory(bContext *C)
|
||||
sfile->params->dir[2] = '\\';
|
||||
sfile->params->dir[3] = '\0';
|
||||
}
|
||||
else if (BLI_path_is_unc(sfile->params->dir)) {
|
||||
BLI_cleanup_unc(sfile->params->dir, FILE_MAX_LIBEXTRA);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
static bool can_create_dir(const char *dir)
|
||||
{
|
||||
/* for UNC paths we need to check whether the parent of the new
|
||||
* directory is a proper directory itself and not a share or the
|
||||
* UNC root (server name) itself. Calling BLI_is_dir does this
|
||||
*/
|
||||
if (BLI_path_is_unc(dir)) {
|
||||
char parent[PATH_MAX];
|
||||
BLI_strncpy(parent, dir, PATH_MAX);
|
||||
BLI_parent_dir(parent);
|
||||
return BLI_is_dir(parent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UNUSED(arg_but))
|
||||
{
|
||||
SpaceFile *sfile = CTX_wm_space_file(C);
|
||||
@@ -1234,6 +1266,13 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
|
||||
* placing cursor at the end */
|
||||
/* UI_textbutton_activate_but(C, but); */
|
||||
}
|
||||
#if defined(WIN32)
|
||||
else if (!can_create_dir(sfile->params->dir)) {
|
||||
const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
|
||||
if (lastdir)
|
||||
BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
|
||||
|
||||
|
Reference in New Issue
Block a user