fix Windows stat() problem, and also fix some path-manipulation bugs related to //?/ paths

svn: r6037
This commit is contained in:
Matthew Flatt 2007-04-25 05:56:50 +00:00
parent 6afb79188d
commit 7c0ea5b79c
2 changed files with 296 additions and 159 deletions

View File

@ -453,11 +453,11 @@
;; Check drive and path->complete-path:
(parameterize ([current-directory "\\\\?\\"])
(test (string->path "\\\\?\\") current-drive)
(test (string->path "\\\\?\\\\a") path->complete-path "\\a")
(test (string->path "\\\\?\\\\\\a") path->complete-path "\\a")
)
(parameterize ([current-directory "\\\\?\\x\\y\\"])
(test (string->path "\\\\?\\") current-drive)
(test (string->path "\\\\?\\\\a") path->complete-path "\\a")
(test (string->path "\\\\?\\\\\\a") path->complete-path "\\a")
)
(parameterize ([current-directory "\\\\?\\d:\\foo"])
;; because it simplifies, \\?\ goes away
@ -471,7 +471,7 @@
)
(parameterize ([current-directory "\\\\?\\REL\\"])
(test (string->path "\\\\?\\") current-drive)
(test (string->path "\\\\?\\\\a") path->complete-path "\\a")
(test (string->path "\\\\?\\\\\\a") path->complete-path "\\a")
)
(parameterize ([current-directory "\\\\?\\REL\\a\\\\"])
(test (string->path "\\\\?\\REL\\a\\\\") current-drive)
@ -604,17 +604,19 @@
(test (string->path "\\\\?\\c:\\b") build-path (coerce "\\\\?\\c:\\\\") (coerce "\\b"))
(test (string->path "\\\\?\\c:\\b\\") build-path (coerce "\\\\?\\c:\\\\") (coerce "\\b\\"))
(test (string->path "\\\\?\\UNC\\goo\\bar\\b") build-path (coerce "\\\\?\\UNC\\goo\\bar") (coerce "\\b"))
(test (string->path "\\\\?\\\\b") build-path (coerce "\\\\?\\") (coerce "\\b"))
(test (string->path "\\\\?\\\\b\\") build-path (coerce "\\\\?\\") (coerce "\\b\\"))
(test (string->path "\\\\?\\\\\\b") build-path (coerce "\\\\?\\") (coerce "\\b"))
(test (string->path "\\\\?\\\\\\b\\") build-path (coerce "\\\\?\\") (coerce "\\b\\"))
(err/rt-test (build-path "\\\\?\\c:" (coerce "\\b")) exn:fail:contract?)
;; Don't allow path addition on bad \\?\ to change the root:
(test (string->path "\\\\?\\\\c") build-path (coerce "\\\\?\\") (coerce "c"))
(test (string->path "\\\\?\\\\UNC") build-path (coerce "\\\\?\\") (coerce "UNC"))
(test (string->path "\\\\?\\\\UNC\\s\\y") build-path (coerce "\\\\?\\UNC") (coerce "s/y"))
(test (string->path "\\\\?\\\\UNC\\s\\y") build-path (coerce "\\\\?\\UNC\\") (coerce "s/y"))
(test (string->path "\\\\?\\\\REL\\s\\y") build-path (coerce "\\\\?\\REL") (coerce "s/y"))
(test (string->path "\\\\?\\\\REL\\s\\y") build-path (coerce "\\\\?\\REL\\") (coerce "s/y"))
(test (string->path "\\\\?\\\\\\c") build-path (coerce "\\\\?\\") (coerce "c"))
(test (string->path "\\\\?\\\\\\c:") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:"))
(test (string->path "\\\\?\\\\\\UNC") build-path (coerce "\\\\?\\") (coerce "UNC"))
(test (string->path "\\\\?\\\\\\UNC\\s\\y") build-path (coerce "\\\\?\\UNC") (coerce "s/y"))
(test (string->path "\\\\?\\\\\\UNC\\s\\y") build-path (coerce "\\\\?\\UNC\\") (coerce "s/y"))
(test (string->path "\\\\?\\\\\\UNC\\s\\y") build-path (coerce "\\\\?\\UNC\\s") (coerce "y"))
(test (string->path "\\\\?\\\\\\REL\\s\\y") build-path (coerce "\\\\?\\REL") (coerce "s/y"))
(test (string->path "\\\\?\\\\\\REL\\s\\y") build-path (coerce "\\\\?\\REL\\") (coerce "s/y"))
(test (string->path "\\\\?\\REL\\\\\\s\\y") build-path (coerce "\\\\?\\REL\\\\") (coerce "s/y"))
(test (string->path "\\\\?\\REL\\x\\\\\\z") build-path (coerce "\\\\?\\REL\\x\\\\") (coerce "z"))
(test (string->path "/apple\\x") build-path (coerce "//apple") (coerce "x"))
@ -623,11 +625,11 @@
(test (string->path "\\?\\a") build-path (coerce "\\\\") (coerce "?") (coerce "a"))
(test (string->path "\\?\\a") build-path (coerce "\\\\?") (coerce "a"))
(test (string->path "\\?\\a\\") build-path (coerce "\\\\?") (coerce "a\\"))
(test (string->path "\\\\?\\\\c:") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:"))
(test (string->path "\\\\?\\\\c:\\") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:\\"))
(test (string->path "\\\\?\\\\c:\\a") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:\\a"))
(test (string->path "\\\\?\\\\REL\\b") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\REL\\b"))
(test (string->path "\\\\?\\\\host\\vol\\a\\") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\\\host\\vol\\a\\"))
(test (string->path "\\\\?\\\\\\c:") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:"))
(test (string->path "\\\\?\\\\\\c:\\") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:\\"))
(test (string->path "\\\\?\\\\\\c:\\a") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\c:\\a"))
(test (string->path "\\\\?\\\\\\REL\\b") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\REL\\b"))
(test (string->path "\\\\?\\\\\\host\\vol\\a\\") build-path (coerce "\\\\?\\") (coerce "\\\\?\\REL\\\\host\\vol\\a\\"))
;; UNC paths can't have "?" for machine or "/" in machine part:
(test (list (string->path "/?/") (string->path "x")) get-base (coerce "//?/x"))
@ -663,7 +665,7 @@
(test (list (string->path "c:/") (string->path "\\\\?\\REL\\\\aux.m.p")) get-base (coerce "c:/aux.m.p/"))
(test (list (string->path "c:/") (string->path "\\\\?\\REL\\\\aux:m")) get-base (coerce "c:/aux:m/"))
(test (list (string->path "../") (string->path "aux.m")) get-base (coerce "../aux.m"))
;; simplify-path leaves literal . and .. alone:
(test (string->path "\\\\?\\c:\\b\\.\\..\\a") simplify-path (coerce "\\\\?\\c:\\b\\.\\..\\a") #f)
(test (string->path "\\\\?\\c:\\B\\.\\..\\a") normal-case-path (coerce "\\\\?\\c:\\B\\.\\..\\a"))
@ -696,11 +698,24 @@
(test (string->path "a\\b\\") simplify-path (coerce "a/b/") #f)
(test (string->path "C:\\") simplify-path (coerce "C://") #f)
(test (string->path "C:\\a\\") simplify-path (coerce "C://a//") #f)
(test (string->path "\\\\?\\\\c:") simplify-path (coerce "\\\\?\\c:") #f))
(test (string->path "\\\\?\\\\\\c:") simplify-path (coerce "\\\\?\\c:") #f)
(test (string->path "\\\\?\\\\\\c:") simplify-path (coerce "\\\\?\\\\c:") #f)
(test (string->path "\\\\?\\\\\\c:") simplify-path (coerce "\\\\?\\\\\\c:") #f))
(test (bytes->path #"\\\\f\\g\\") simplify-path (coerce "\\\\f\\g") #f)
(test (bytes->path #"\\\\f\\g\\") simplify-path (coerce "//f/g") #f)
(test (bytes->path #"\\\\?\\\\\\c:\\") simplify-path (coerce "\\\\?\\\\\\c:\\") #f)
(test (bytes->path #"\\\\?\\\\\\c:") simplify-path (coerce "\\\\?\\\\\\c:") #f)
(test (bytes->path #"\\\\?\\c:\\a\\b//c\\d") expand-path (coerce "\\\\?\\c:\\a\\b//c\\d"))
(test (bytes->path #"\\\\?\\UNC\\a\\b/c\\") simplify-path (coerce "\\\\?\\\\UNC\\a\\b/c") #f)
(test (bytes->path #"\\\\a\\b\\") simplify-path (coerce "\\\\?\\\\UNC\\a\\b") #f)
(test (bytes->path #"\\\\?\\UNC\\a\\b/c\\") simplify-path (coerce "\\\\?\\UnC\\a\\b/c") #f)
(test (bytes->path #"\\\\a\\b\\") simplify-path (coerce "\\\\?\\UnC\\a\\b") #f)
(test (bytes->path #"\\\\?\\c:\\a/b") simplify-path (coerce "\\\\?\\c:\\a/b") #f)
(test (bytes->path #"..\\") simplify-path (coerce "\\\\?\\REL\\..") #f)
(test (bytes->path #"..\\") simplify-path (coerce "\\\\?\\REL\\..\\") #f)
(when (eq? 'windows (system-type))
@ -724,17 +739,19 @@
(test (bytes->path #"\\\\?\\REL\\\\.") simplify-path (coerce "\\\\?\\REL\\.") #F)
(test (bytes->path #"\\\\?\\REL\\\\:\\b") simplify-path (coerce "\\\\?\\REL\\:\\b") #f)
(test (bytes->path #"\\\\?\\REL\\\\:") simplify-path (coerce "\\\\?\\REL\\:") #F)
(test (bytes->path #"\\\\?\\\\REL") simplify-path (coerce "\\\\?\\REL") #F)
(test (bytes->path #"\\\\?\\\\REL") simplify-path (coerce "\\\\?\\\\REL") #F)
(test (bytes->path #"\\\\?\\\\\\REL") simplify-path (coerce "\\\\?\\REL") #F)
(test (bytes->path #"\\\\?\\\\\\REL") simplify-path (coerce "\\\\?\\\\REL") #F)
(test (bytes->path #"\\\\?\\\\\\REL") simplify-path (coerce "\\\\?\\\\\\REL") #F)
(test (bytes->path #"C:\\a\\b") simplify-path (coerce "\\\\?\\C:\\a\\b") #f)
(test (bytes->path #"C:\\a") simplify-path (coerce "\\\\?\\C:\\a") #f)
(test (bytes->path #"\\\\?\\C:\\a ") simplify-path (coerce "\\\\?\\C:\\a ") #f)
(test (bytes->path #"\\\\?\\\\C:a\\b") simplify-path (coerce "\\\\?\\C:a\\b") #f)
(test (bytes->path #"\\\\?\\\\C:a\\b") simplify-path (coerce "\\\\?\\\\C:a\\b") #f)
(test (bytes->path #"\\\\?\\\\C:") simplify-path (coerce "\\\\?\\C:") #f)
(test (bytes->path #"\\\\?\\\\C:") simplify-path (coerce "\\\\?\\\\C:") #f)
(test (bytes->path #"\\\\?\\\\a\\y") simplify-path (coerce "\\\\?\\a\\y") #f)
(test (bytes->path #"\\\\?\\\\a\\y") simplify-path (coerce "\\\\?\\\\a\\y") #f)
(test (bytes->path #"\\\\?\\\\\\C:a\\b") simplify-path (coerce "\\\\?\\C:a\\b") #f)
(test (bytes->path #"\\\\?\\\\\\C:a\\b") simplify-path (coerce "\\\\?\\\\C:a\\b") #f)
(test (bytes->path #"\\\\?\\\\\\C:") simplify-path (coerce "\\\\?\\C:") #f)
(test (bytes->path #"\\\\?\\\\\\C:") simplify-path (coerce "\\\\?\\\\C:") #f)
(test (bytes->path #"\\\\?\\\\\\a\\y") simplify-path (coerce "\\\\?\\a\\y") #f)
(test (bytes->path #"\\\\?\\\\\\a\\y") simplify-path (coerce "\\\\?\\\\a\\y") #f)
(test (bytes->path #"\\\\?\\\\\\a\\y") simplify-path (coerce "\\\\?\\\\\\a\\y") #f)
(test (bytes->path #"\\\\?\\REL\\a\\y\\\\") simplify-path (coerce "\\\\?\\REL\\a\\y\\\\") #f)
(test (bytes->path #"\\\\?\\REL\\a\\\\\\y") simplify-path (coerce "\\\\?\\REL\\a\\\\\\y") #f)
(test (bytes->path #"\\a") simplify-path (coerce "\\\\?\\RED\\a") #f)

View File

@ -126,6 +126,7 @@ long scheme_creator_id = 'MzSc';
#define DOS_FN_SEP '\\'
#define IS_A_DOS_SEP(x) (((x) == '/') || ((x) == '\\'))
#define IS_A_DOS_PRIM_SEP(x) ((x) == '\\')
#define IS_A_DOS_X_SEP(prim, x) (prim ? IS_A_DOS_PRIM_SEP(x) : IS_A_DOS_SEP(x))
#define FN_SEP(kind) ((kind == SCHEME_UNIX_PATH_KIND) ? UNIX_FN_SEP : DOS_FN_SEP)
#define IS_A_SEP(kind, x) ((kind == SCHEME_UNIX_PATH_KIND) ? IS_A_UNIX_SEP(x) : IS_A_DOS_SEP(x))
@ -1214,7 +1215,7 @@ static int check_dos_slashslash_qm(const char *next, int len,
If it's a \\?\ path, then drive_end is set to the first character
after the root specification. For example, if the drive is
terminated by \\\ (a weird "root), then drive_end is set to after
terminated by \\\ (a weird "root"), then drive_end is set to after
the third \. If the drive is \\?\C:\, then drive_end is after the
last slash, unless thre's one extra slash, in which case drive_end
is after that slash, too. In the case of \\?\UNC\..., drive_end
@ -1237,17 +1238,24 @@ static int check_dos_slashslash_qm(const char *next, int len,
&& (next[1] == '\\')
&& (next[2] == '?')
&& (next[3] == '\\')) {
int base;
if (!drive_end && !clean_start && !add_sep)
return 1;
if (next[4] == '\\')
base = 5;
else
base = 4;
/* If there's two backslashes in a row at the end, count everything
as the drive. There's an exception: two backslashes are ok
at the end in the form \\?\C:\\ */
as the drive. There are two exceptions: two backslashes are ok
at the end in the form \\?\C:\\, and \\?\\\ is \\?\ */
if ((len > 5)
&& (next[len - 1] == '\\')
&& (next[len - 2] == '\\')) {
if ((len != 8)
|| !is_drive_letter(next[4])
|| (next[5] != ':')) {
if (len == 6) {
/* \\?\ is the root */
} else if ((len != 8)
|| !is_drive_letter(next[base])
|| (next[base+1] != ':')) {
if (drive_end)
*drive_end = len;
if (clean_start)
@ -1259,9 +1267,9 @@ static int check_dos_slashslash_qm(const char *next, int len,
}
/* If there's three backslashes in a row, count everything
up to the slashes as the drive. */
{
if (len > 6) {
int i;
for (i = len; --i > 3; ) {
for (i = len; --i > 5; ) {
if ((next[i] == '\\')
&& (next[i-1] == '\\')
&& (next[i-2] == '\\')) {
@ -1275,31 +1283,32 @@ static int check_dos_slashslash_qm(const char *next, int len,
}
if ((len > 6)
&& is_drive_letter(next[4])
&& next[5] == ':'
&& next[6] == '\\') {
&& is_drive_letter(next[base])
&& next[base+1] == ':'
&& next[base+2] == '\\') {
if (clean_start)
*clean_start = 6;
*clean_start = base+2;
if (drive_end) {
if ((len > 7) && next[7] == '\\')
*drive_end = 8;
if ((len > base+3) && next[base+3] == '\\')
*drive_end = base+4;
else
*drive_end = 7;
*drive_end = base+3;
}
} else if ((len > 7)
&& (next[4] == 'U')
&& (next[5] == 'N')
&& (next[6] == 'C')
&& (next[7] == '\\')
} else if ((len > base+3)
&& ((next[base] == 'U') || (next[base] == 'u'))
&& ((next[base+1] == 'N') || (next[base+1] == 'n'))
&& ((next[base+2] == 'C') || (next[base+2] == 'c'))
&& (next[base+3] == '\\')
&& check_dos_slashslash_drive(next,
(((len > 8) && (next[8] == '\\'))
? 9
: 8),
(((len > (base+4)) && (next[base+4] == '\\'))
? base+5
: base+4),
len, drive_end, 0, 1)) {
/* drive_end set by check_dos_slashslash_drive */
if (clean_start)
*clean_start = 7;
} else if ((len > 8)
*clean_start = base+3;
} else if ((base == 4)
&& (len > 8)
&& (next[4] == 'R')
&& (next[5] == 'E')
&& ((next[6] == 'L') || (next[6] == 'D'))
@ -1314,7 +1323,8 @@ static int check_dos_slashslash_qm(const char *next, int len,
if (drive_end)
*drive_end = 4;
if (clean_start) {
if ((len == 5) && (next[4] == '\\'))
if (((len == 5) && (next[4] == '\\'))
|| ((len == 6) && (next[4] == '\\') && (next[5] == '\\')))
*clean_start = 3;
else
*clean_start = 4;
@ -1809,6 +1819,12 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
if (is_drive_letter(filename[4])
&& (filename[5] == ':'))
drive_end = 6;
} else if (drive_end == 9) {
/* For \\?\\c:\\ path, start clean up after colon. */
if ((filename[4] == '\\')
&& is_drive_letter(filename[5])
&& (filename[6] == ':'))
drive_end = 7;
} else {
drive_end = clean_start;
}
@ -1823,11 +1839,11 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
insert_initial_sep = 1;
fixit = 1;
} else {
int found_slash = 0;
int found_slash = 0, prim_only = drive_end;
for (i = ilen; i-- > drive_end; ) {
if (IS_A_DOS_SEP(filename[i])) {
if (IS_A_DOS_SEP(filename[i - 1])) {
if (IS_A_DOS_X_SEP(prim_only, filename[i])) {
if (IS_A_DOS_X_SEP(prim_only, filename[i - 1])) {
if ((i > 1) || !found_slash)
fixit = 1;
break;
@ -1838,7 +1854,7 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
}
if (fixit) {
int pos;
int pos, prim_only = drive_end;
char *naya;
if (expanded)
@ -1859,8 +1875,8 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
}
while (i < ilen) {
if (IS_A_DOS_SEP(filename[i])
&& IS_A_DOS_SEP(filename[i + 1])) {
if (IS_A_DOS_X_SEP(prim_only, filename[i])
&& IS_A_DOS_X_SEP(prim_only, filename[i + 1])) {
i++;
} else
naya[pos++] = filename[i++];
@ -1872,12 +1888,22 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
if (drive_end == 4) {
/* If the root was \\?\, there's a chance that we removed a
backslash and changed the root. In that case, add a \ after \\?\: */
backslash and changed the root. In that case, add two \\s after \\?\: */
check_dos_slashslash_qm(filename, ilen, &drive_end, NULL, NULL);
if (drive_end != 4) {
/* There's room to expand, because insert_initial_sep couldn't be -1. */
memmove(filename + 5, filename + 4, ilen - 3);
filename[4] = '\\'; /* Actually, this is redundant. */
if (filename[4] == '\\') {
/* Need one more */
memmove(filename + 5, filename + 4, ilen - 3);
filename[4] = '\\'; /* Actually, this is redundant. */
ilen += 1;
} else {
/* Need two more */
memmove(filename + 6, filename + 4, ilen - 3);
filename[4] = '\\'; /* Actually, this is redundant. */
filename[5] = '\\';
ilen += 2;
}
}
}
}
@ -2016,101 +2042,67 @@ static int UNC_stat(char *dirname, int len, int *flags, int *isdir, Scheme_Objec
mzlonglong *filesize)
/* dirname must be absolute */
{
int strip_end, strip_char;
struct MSC_IZE(_stat64) buf;
int v;
/* Note: stat() doesn't work with UNC "drive" names or \\?\ paths.
Also, stat() doesn't distinguish between the ability to
list a directory's content and the fact to detect that the
directory exists. So, we use GetFileAttributesExW(). */
char *copy;
WIN32_FILE_ATTRIBUTE_DATA fd;
int must_be_dir = 0;
if (isdir)
*isdir = 0;
if (date)
*date = scheme_false;
if ((len > 1) && IS_A_DOS_SEP(dirname[0])
&& (check_dos_slashslash_qm(dirname, len, NULL, NULL, NULL) /* dirname is absolute */
|| check_dos_slashslash_drive(dirname, 0, len, NULL, 1, 0))) {
/* stat doesn't work with UNC "drive" names or \\?\ paths */
char *copy;
WIN32_FILE_ATTRIBUTE_DATA fd;
int must_be_dir = 0;
copy = scheme_malloc_atomic(len + 14);
if (check_dos_slashslash_qm(dirname, len, NULL, NULL, NULL)) {
memcpy(copy, dirname, len + 1);
} else {
memcpy(copy, dirname, len + 1);
while (IS_A_DOS_SEP(copy[len - 1])) {
--len;
copy[len] = 0;
must_be_dir = 1;
}
}
/* If we ended up with "\\?\X:", then drop the "\\?\" */
if ((copy[2] == '?') && is_drive_letter(copy[4]) && (copy[5] == ':') && !copy[6]) {
memmove(copy, copy + 4, len - 4);
len -= 4;
copy = scheme_malloc_atomic(len + 14);
if (check_dos_slashslash_qm(dirname, len, NULL, NULL, NULL)) {
memcpy(copy, dirname, len + 1);
} else {
memcpy(copy, dirname, len + 1);
while (IS_A_DOS_SEP(copy[len - 1])) {
--len;
copy[len] = 0;
must_be_dir = 1;
}
if (!GetFileAttributesExW(WIDE_PATH(copy), GetFileExInfoStandard, &fd)) {
errno = -1;
return 0;
} else {
if (must_be_dir && !(GET_FF_ATTRIBS(fd) & FF_A_DIR))
return 0;
if (flags)
*flags = MZ_UNC_READ | MZ_UNC_EXEC | ((GET_FF_ATTRIBS(fd) & FF_A_RDONLY) ? 0 : MZ_UNC_WRITE);
if (date) {
Scheme_Object *dt;
time_t mdt;
mdt = GET_FF_MODDATE(fd);
dt = scheme_make_integer_value_from_time(mdt);
*date = dt;
}
if (isdir) {
*isdir = (GET_FF_ATTRIBS(fd) & FF_A_DIR);
}
if (filesize) {
*filesize = ((mzlonglong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
}
return 1;
}
} else if ((len > 1) && (dirname[len - 1] == '\\' || dirname[len - 1] == '/')
&& (dirname[len - 2] != ':')) {
strip_end = len - 1;
strip_char = dirname[strip_end];
dirname[strip_end] = 0;
if (isdir)
*isdir = 1;
} else
strip_end = strip_char = 0;
while (1) {
v = !MSC_W_IZE(stat64)(MSC_WIDE_PATH(dirname), &buf);
if (v || (errno != EINTR))
break;
}
if (v) {
if (isdir && S_ISDIR(buf.st_mode))
*isdir = 1;
/* If we ended up with "\\?\X:", then drop the "\\?\" */
if ((copy[0] == '\\')&& (copy[1] == '\\') && (copy[2] == '?') && (copy[3] == '\\')
&& is_drive_letter(copy[4]) && (copy[5] == ':') && !copy[6]) {
memmove(copy, copy + 4, len - 4);
len -= 4;
copy[len] = 0;
}
/* If we ended up with "\\?\X:", then drop the "\\?\\" */
if ((copy[0] == '\\') && (copy[1] == '\\') && (copy[2] == '?') && (copy[3] == '\\')
&& (copy[4] == '\\') && is_drive_letter(copy[5]) && (copy[6] == ':') && !copy[7]) {
memmove(copy, copy + 5, len - 5);
len -= 5;
copy[len] = 0;
}
if (!GetFileAttributesExW(WIDE_PATH(copy), GetFileExInfoStandard, &fd)) {
errno = -1;
return 0;
} else {
if (must_be_dir && !(GET_FF_ATTRIBS(fd) & FF_A_DIR))
return 0;
if (flags)
*flags = MZ_UNC_READ | MZ_UNC_EXEC | ((GET_FF_ATTRIBS(fd) & FF_A_RDONLY) ? 0 : MZ_UNC_WRITE);
if (date) {
Scheme_Object *dt;
dt = scheme_make_integer_value_from_time(buf.st_mtime);
(*date) = dt;
time_t mdt;
mdt = GET_FF_MODDATE(fd);
dt = scheme_make_integer_value_from_time(mdt);
*date = dt;
}
if (flags) {
if (buf.st_mode & MSC_IZE(S_IREAD))
*flags |= MZ_UNC_READ | MZ_UNC_EXEC;
if (buf.st_mode & MSC_IZE(S_IWRITE))
*flags |= MZ_UNC_WRITE;
if (isdir) {
*isdir = (GET_FF_ATTRIBS(fd) & FF_A_DIR);
}
if (filesize)
*filesize = buf.st_size;
if (filesize) {
*filesize = ((mzlonglong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
}
return 1;
}
if (strip_end)
dirname[strip_end] = strip_char;
return v;
}
#endif
@ -2561,6 +2553,7 @@ static Scheme_Object *do_build_path(int argc, Scheme_Object **argv, int idelta,
int first_len = 0;
int needs_extra_slash = 0;
int pre_unc = 0;
int pre_qm = 0;
const char *who = (idelta ? "build-path/convention-type" : "build-path");
str = buffer;
@ -2816,9 +2809,10 @@ static Scheme_Object *do_build_path(int argc, Scheme_Object **argv, int idelta,
no_sep = 1;
} else {
/* One last possibility: str is \\?\ (which counts as a bizaare
root). We need an extra slash. */
root). We need two extra slashes. */
if (!new_rel_base && (pos == 4)) {
str[pos++] = '\\';
str[pos++] = '\\';
}
}
first_len = pos + len;
@ -2829,6 +2823,7 @@ static Scheme_Object *do_build_path(int argc, Scheme_Object **argv, int idelta,
first_len = len;
}
} else {
/* non-REL/RED \\?\ path */
is_drive = (drive_end == len);
needs_extra_slash = plus_sep;
if (!i) {
@ -2890,10 +2885,20 @@ static Scheme_Object *do_build_path(int argc, Scheme_Object **argv, int idelta,
}
if (kind == SCHEME_WINDOWS_PATH_KIND) {
if (i)
if (i) {
pre_unc = check_dos_slashslash_drive(str, 0, pos, NULL, 0, 0);
else
if (!pre_unc) {
int de;
if (check_dos_slashslash_qm(str, pos, &de, NULL, NULL)) {
if (de == 4) /* \\?\ */
pre_qm = 1;
}
} else
pre_qm = 0;
} else {
pre_unc = 1;
pre_qm = 0;
}
if (no_final_simplify
&& (len == 2)
@ -2963,6 +2968,46 @@ static Scheme_Object *do_build_path(int argc, Scheme_Object **argv, int idelta,
memmove(str, str+1, pos - 1);
--pos;
}
if (pre_qm) {
int de;
/* Normalize path separators for the addition: */
{
int i;
for (i = first_len; i < pos; i++) {
if (str[i] == '/') {
str[i] = '\\';
}
}
}
/* check the \\?\ parsing */
check_dos_slashslash_qm(str, pos, &de, NULL, NULL);
if (de != 4) {
/* Added to \\?\ to get something that now looks like
a \\?\UNC path. Insert a backslash or two. */
int amt = ((str[4] == '\\') ? 1 : 2);
if (pos + amt >= alloc) {
char *naya;
int newalloc;
newalloc = 2 * alloc;
naya = (char *)scheme_malloc_atomic(newalloc);
memcpy(naya, str, pos);
alloc = newalloc;
str = naya;
}
memmove(str + 4 + amt, str + 4, pos - 4);
str[4] = '\\';
if (amt == 2)
str[5] = '\\';
pos += amt;
first_len += amt;
}
}
if (needs_extra_slash) {
if (needs_extra_slash >= pos)
str[pos++] = '\\';
@ -3914,7 +3959,7 @@ static Scheme_Object *simplify_qm_path(Scheme_Object *path)
char *s = SCHEME_PATH_VAL(path);
int drive_end, clean_start, len = SCHEME_PATH_LEN(path), fixed = 0, i;
int drop_extra_slash = -1, set_slash = -1, element_start;
int found_bad = 0, start_special_check = 0, is_dir = 0;
int found_bad = 0, start_special_check = 0, is_dir = 0, norm_unc = 0, drop_ss_slash = 0;
if ((s[len - 1] == '\\')
&& (s[len - 2] != '\\')
@ -3930,6 +3975,14 @@ static Scheme_Object *simplify_qm_path(Scheme_Object *path)
/* Maybe don't need \\?\ for \\?\C:\... */
start_special_check = 7;
drive_end = 4;
} else if ((drive_end == 8)
&& (s[4] == '\\')
&& is_drive_letter(s[5])
&& (s[6] == ':')) {
/* Maybe don't need \\?\\ for \\?\\C:\... */
start_special_check = 8;
drive_end = 5;
drop_ss_slash = 1;
} else if (drive_end == -2) {
/* \\?\RED\ */
int lit_start;
@ -3952,15 +4005,29 @@ static Scheme_Object *simplify_qm_path(Scheme_Object *path)
is_dir = 1;
}
} else if ((clean_start == 7)
&& (s[4] == 'U')
&& (s[5] == 'N')
&& (s[6] == 'C')) {
&& ((s[4] == 'U') || (s[4] == 'u'))
&& ((s[5] == 'N') || (s[5] == 'n'))
&& ((s[6] == 'C') || (s[6] == 'c'))) {
if (drive_end == len) {
is_dir = 1;
}
drive_end = 6;
start_special_check = 7; /* \\?\UNC */
set_slash = 6;
norm_unc = 1;
} else if ((clean_start == 8)
&& (s[4] == '\\')
&& ((s[5] == 'U') || (s[5] == 'u'))
&& ((s[6] == 'N') || (s[6] == 'n'))
&& ((s[7] == 'C') || (s[7] == 'c'))) {
if (drive_end == len) {
is_dir = 1;
}
drive_end = 7;
start_special_check = 8; /* \\?\\UNC */
set_slash = 7;
norm_unc = 1;
drop_ss_slash = 1;
} else {
/* We have a weird root. Give up. */
found_bad = 1;
@ -4001,10 +4068,33 @@ static Scheme_Object *simplify_qm_path(Scheme_Object *path)
}
if (found_bad) {
if (norm_unc) {
if ((s[4 + drop_ss_slash] == 'U')
&& (s[5 + drop_ss_slash] == 'N')
&& (s[6 + drop_ss_slash] == 'C'))
norm_unc = 0;
}
if (norm_unc || drop_ss_slash) {
if (!fixed) {
char *naya;
naya = (char *)scheme_malloc_atomic(len);
memcpy(naya, s, len);
s = naya;
fixed = 1;
}
if (drop_ss_slash) {
memmove(s + 3, s + 4, len - 4);
len--;
}
if (norm_unc) {
s[4] = 'U';
s[5] = 'N';
s[6] = 'C';
}
}
if (fixed)
return scheme_make_sized_offset_kind_path(s, 0, len, 1, SCHEME_WINDOWS_PATH_KIND);
else
return path;
path = scheme_make_sized_offset_kind_path(s, 0, len, 1, SCHEME_WINDOWS_PATH_KIND);
return path;
} else {
if (drop_extra_slash > -1) {
char *naya;
@ -4067,7 +4157,18 @@ static Scheme_Object *do_simplify_path(Scheme_Object *path, Scheme_Object *cycle
} else if (add_sep) {
int len = SCHEME_PATH_LEN(path);
if ((add_sep < len) && (s[add_sep] != '\\')) {
/* Add a \, as in \\?\c -> \\?\\c */
/* Add two \, as in \\?\c -> \\?\\\c */
char *naya;
naya = (char *)scheme_malloc_atomic(len + 3);
memcpy(naya, s, add_sep);
naya[add_sep] = '\\';
naya[add_sep+1] = '\\';
memcpy(naya + add_sep + 2, s + add_sep, len + 1 - add_sep);
len += 2;
path = scheme_make_sized_offset_kind_path(naya, 0, len, 0, SCHEME_WINDOWS_PATH_KIND);
}
if ((add_sep < len) && (s[add_sep] == '\\') && (s[add_sep+1] != '\\')) {
/* Add \, as in \\?\\c -> \\?\\\c */
char *naya;
naya = (char *)scheme_malloc_atomic(len + 2);
memcpy(naya, s, add_sep);
@ -4091,17 +4192,31 @@ static Scheme_Object *do_simplify_path(Scheme_Object *path, Scheme_Object *cycle
if (kind == SCHEME_WINDOWS_PATH_KIND) {
if (!skip && check_dos_slashslash_qm(s, len, NULL, NULL, NULL)) {
if (!force_rel_up)
return simplify_qm_path(path);
else
if (!force_rel_up) {
int drive_end;
path = simplify_qm_path(path);
len = SCHEME_PATH_LEN(path);
if (check_dos_slashslash_qm(SCHEME_PATH_VAL(path), len, &drive_end, NULL, NULL)) {
/* If it's a drive... */
if (drive_end == len) {
/* Make it a directory path. */
path = scheme_path_to_directory_path(path);
}
}
return path;
} else {
/* force_rel_up means that we want a directory: */
return scheme_path_to_directory_path(path);
}
}
if (!skip && check_dos_slashslash_drive(s, 0, len, NULL, 1, 0)) {
/* Remove trailing slashes, if any: */
/* A UNC drive (with no further elements).
Remove extra trailing slashes, if any... */
for (i = len; IS_A_DOS_SEP(s[i-1]); i--) { }
if (i != len) {
if (i < len - 1) {
path = scheme_make_sized_offset_kind_path(s, 0, i, 1, SCHEME_WINDOWS_PATH_KIND);
}
/* ... but make it a directory path. */
path = scheme_path_to_directory_path(path);
}
@ -4555,8 +4670,13 @@ static Scheme_Object *do_directory_list(int break_ok, int argc, Scheme_Object *a
}
hfile = FIND_FIRST(WIDE_PATH(pattern), &info);
if (FIND_FAILED(hfile))
if (FIND_FAILED(hfile)) {
scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
"directory-list: could not open \"%q\" (%E)",
filename,
GetLastError());
return scheme_null;
}
do {
if ((GET_FF_NAME(info)[0] == '.')