Skip to content

Commit

Permalink
Fix list.files() on Windows to see files with names longer than 260 b…
Browse files Browse the repository at this point in the history
…ytes

(yet still in the 260 character limit). Reported by Ivan Krylov.


git-svn-id: https://svn.r-project.org/R/trunk@84960 00db46b3-68df-0310-9c12-caf00c1e9a41
  • Loading branch information
kalibera committed Aug 16, 2023
1 parent 6134200 commit bee54bf
Showing 1 changed file with 64 additions and 55 deletions.
119 changes: 64 additions & 55 deletions src/main/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,9 @@ attribute_hidden SEXP do_direxists(SEXP call, SEXP op, SEXP args, SEXP rho)
R_opendir/R_readdir/R_closedir implement a subset of the functionality,
on Unix they fall back to POSIX API, on Windows they support long paths.
Unlike MinGW-W64, they use wide-string search functions internally to
support file names up to MAX_PATH wide characters.
Note that d_name pointer may change between readdir operations.
R_wopendir/R_wreaddir/R_wclosedir are wide-string variants for Windows. */

Expand All @@ -1100,15 +1103,47 @@ attribute_hidden SEXP do_direxists(SEXP call, SEXP op, SEXP args, SEXP rho)

struct R_DIR_INTERNAL {
#ifdef Win32
char *pattern;
WIN32_FIND_DATA fdata;
wchar_t *pattern;
WIN32_FIND_DATAW fdata;
HANDLE hfind;
R_StringBuffer cbuff;
#else
DIR *dirp;
#endif
struct R_dirent de;
};

#ifdef Win32
static wchar_t* search_wpattern(const wchar_t *name)
{
const void *vmax = vmaxget();
wchar_t *apath = R_getFullPathNameW(name);
if (!apath) {
errno = EFAULT;
vmaxset(vmax);
return NULL;
}
size_t len = wcslen(apath);
/* <slash><star><null> */
wchar_t *pattern = malloc((len + 3) * sizeof(wchar_t));
if (!pattern) {
errno = EFAULT;
vmaxset(vmax);
return NULL;
}
memcpy(pattern, apath, len * sizeof(wchar_t));
/* apath is not D: (that would have been expanded) */

/* add separator if not present and pattern not empty */
if (len > 0 && pattern[len-1] != L'\\' && pattern[len-1] != L'/')
pattern[len++] = L'/';
pattern[len++] = L'*';
pattern[len] = L'\0';
vmaxset(vmax);
return pattern;
}
#endif

R_DIR *R_opendir(const char *name)
{
R_DIR *rdir = malloc(sizeof(R_DIR));
Expand All @@ -1128,34 +1163,25 @@ R_DIR *R_opendir(const char *name)
free(rdir);
return NULL;
}

const void *vmax = vmaxget();
char *apath = R_getFullPathName(name);
if (!apath) {
errno = EFAULT;
vmaxset(vmax);
int nc = (int) mbstowcs(NULL, name, 0);
if (nc < 0) {
errno = ENOENT;
free(rdir);
return NULL;
}
size_t len = strlen(apath);
char *pattern = malloc(len + 3); /* <slash><star><null> */
if (!pattern) {
errno = EFAULT;
const void *vmax = vmaxget();
wchar_t *wname = (wchar_t *) R_alloc(nc + 1, sizeof(wchar_t));
mbstowcs(wname, name, nc + 1);
rdir->pattern = search_wpattern(wname); /* malloc'd */
if (!rdir->pattern) {
vmaxset(vmax);
free(rdir);
return NULL;
}
memcpy(pattern, apath, len);
/* apath is not D: (that would have been expanded) */

/* add separator if not present and pattern not empty */
if (len > 0 && pattern[len-1] != '\\' && pattern[len-1] != '/')
pattern[len++] = '/';
pattern[len++] = '*';
pattern[len] = '\0';

rdir->pattern = pattern;
}
rdir->hfind = INVALID_HANDLE_VALUE;
rdir->cbuff.data = NULL;
rdir->cbuff.bufsize = 0;
rdir->cbuff.defaultSize = MAXELTSIZE;
vmaxset(vmax);
#else
rdir->dirp = opendir(name);
Expand All @@ -1176,24 +1202,30 @@ struct R_dirent *R_readdir(R_DIR *rdir)
#ifdef Win32
if (rdir->pattern) {
/* starting the search */
rdir->hfind = FindFirstFile(rdir->pattern, &rdir->fdata);
rdir->hfind = FindFirstFileW(rdir->pattern, &rdir->fdata);
free(rdir->pattern);
rdir->pattern = NULL;
if (rdir->hfind == INVALID_HANDLE_VALUE)
/* keep errno, no files, even though not likely (., ..) */
return NULL;
rdir->de.d_name = (char *)&rdir->fdata.cFileName;
return &rdir->de;
} else if (rdir->hfind != INVALID_HANDLE_VALUE) {
/* continuing the search */
if (!FindNextFile(rdir->hfind, &rdir->fdata))
if (!FindNextFileW(rdir->hfind, &rdir->fdata))
/* keep errno, no more files */
return NULL;
return &rdir->de;
} else {
errno = EFAULT;
return NULL;
}
wchar_t *wname = (wchar_t *)&rdir->fdata.cFileName;
int nb = (int) wcstombs(NULL, wname, 0);
if (nb < 0)
/* invalid strings stop the search */
return NULL;
R_AllocStringBuffer(nb + 1, &rdir->cbuff);
wcstombs(rdir->cbuff.data, wname, nb + 1);
rdir->de.d_name = rdir->cbuff.data;
return &rdir->de;
#else
struct dirent *de;
de = readdir(rdir->dirp);
Expand All @@ -1212,6 +1244,7 @@ int R_closedir(R_DIR *rdir)
return -1;
}
#ifdef Win32
R_FreeStringBuffer(&rdir->cbuff);
if (rdir->pattern)
free(rdir->pattern);
BOOL r = 0;
Expand Down Expand Up @@ -1258,36 +1291,12 @@ attribute_hidden R_WDIR *R_wopendir(const wchar_t *name)
free(rdir);
return NULL;
}

const void *vmax = vmaxget();
wchar_t *apath = R_getFullPathNameW(name);
if (!apath) {
errno = EFAULT;
vmaxset(vmax);
free(rdir);
return NULL;
}
size_t len = wcslen(apath);
/* <slash><star><null> */
wchar_t *pattern = malloc((len + 3) * sizeof(wchar_t));
if (!pattern) {
errno = EFAULT;
vmaxset(vmax);
rdir->pattern = search_wpattern(name); /* malloc'd */
if (!rdir->pattern) {
free(rdir);
return NULL;
}
memcpy(pattern, apath, len * sizeof(wchar_t));
/* apath is not D: (that would have been expanded) */

/* add separator if not present and pattern not empty */
if (len > 0 && pattern[len-1] != L'\\' && pattern[len-1] != L'/')
pattern[len++] = L'/';
pattern[len++] = L'*';
pattern[len] = L'\0';

rdir->pattern = pattern;
}
rdir->hfind = INVALID_HANDLE_VALUE;
vmaxset(vmax);
return rdir;
}

Expand Down

0 comments on commit bee54bf

Please sign in to comment.