summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST1
-rw-r--r--t/harness1
-rwxr-xr-xt/win32/longpath.t52
-rw-r--r--win32/win32.c89
4 files changed, 111 insertions, 32 deletions
diff --git a/MANIFEST b/MANIFEST
index 9c69348ce0..698e613cfb 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2552,6 +2552,7 @@ t/uni/lower.t See if Unicode casing works
t/uni/sprintf.t See if Unicode sprintf works
t/uni/title.t See if Unicode casing works
t/uni/upper.t See if Unicode casing works
+t/win32/longpath.t Test if Win32::GetLongPathName() works
t/x2p/s2p.t See if s2p/psed work
taint.c Tainting code
thrdvar.h Per-thread variables
diff --git a/t/harness b/t/harness
index a19363af3e..e9cf2ed925 100644
--- a/t/harness
+++ b/t/harness
@@ -57,6 +57,7 @@ if (@ARGV) {
push @tests, <op/*.t>;
push @tests, <uni/*.t>;
push @tests, <lib/*.t>;
+ push @tests, <win32/*.t> if $^O eq 'MSWin32';
use File::Spec;
my $updir = File::Spec->updir;
my $mani = File::Spec->catfile(File::Spec->updir, "MANIFEST");
diff --git a/t/win32/longpath.t b/t/win32/longpath.t
new file mode 100755
index 0000000000..d31a5b4dce
--- /dev/null
+++ b/t/win32/longpath.t
@@ -0,0 +1,52 @@
+#!perl -w
+
+# tests for Win32::GetLongPathName()
+
+$^O =~ /^MSWin/ or print("1..0 # not win32\n" ), exit;
+
+my @paths = qw(
+ /
+ //
+ .
+ ..
+ c:
+ c:/
+ c:./
+ c:/.
+ c:/..
+ c:./..
+ //./
+ //.
+ //..
+ //./..
+);
+push @paths, map { my $x = $_; $x =~ s,/,\\,g; $x } @paths;
+push @paths, qw(
+ ../\
+ c:.\\../\
+ c:/\..//
+ c://.\/./\
+ \\.\\../\
+ //\..//
+ //.\/./\
+);
+
+my $drive = $ENV{SystemDrive};
+if ($drive) {
+ for (@paths) {
+ s/^c:/$drive/;
+ }
+ push @paths, $ENV{SystemRoot} if $ENV{SystemRoot};
+}
+my %expect;
+@expect{@paths} = map { my $x = $_; $x =~ s,(.[/\\])[/\\]+,$1,g; $x } @paths;
+
+print "1.." . @paths . "\n";
+my $i = 1;
+for (@paths) {
+ my $got = Win32::GetLongPathName($_);
+ print "# '$_' => expect '$expect{$_}' => got '$got'\n";
+ print "not " unless $expect{$_} eq $got;
+ print "ok $i\n";
+ ++$i;
+}
diff --git a/win32/win32.c b/win32/win32.c
index 31a1496fa0..fd44c5f10a 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -1284,6 +1284,18 @@ win32_stat(const char *path, struct stat *sbuf)
return res;
}
+#define isSLASH(c) ((c) == '/' || (c) == '\\')
+#define SKIP_SLASHES(s) \
+ STMT_START { \
+ while (*(s) && isSLASH(*(s))) \
+ ++(s); \
+ } STMT_END
+#define COPY_NONSLASHES(d,s) \
+ STMT_START { \
+ while (*(s) && !isSLASH(*(s))) \
+ *(d)++ = *(s)++; \
+ } STMT_END
+
/* Find the longname of a given path. path is destructively modified.
* It should have space for at least MAX_PATH characters. */
DllExport char *
@@ -1299,61 +1311,74 @@ win32_longpath(char *path)
return Nullch;
/* drive prefix */
- if (isALPHA(path[0]) && path[1] == ':' &&
- (path[2] == '/' || path[2] == '\\'))
- {
+ if (isALPHA(path[0]) && path[1] == ':') {
start = path + 2;
*tmpstart++ = path[0];
*tmpstart++ = ':';
}
/* UNC prefix */
- else if ((path[0] == '/' || path[0] == '\\') &&
- (path[1] == '/' || path[1] == '\\'))
- {
+ else if (isSLASH(path[0]) && isSLASH(path[1])) {
start = path + 2;
*tmpstart++ = path[0];
*tmpstart++ = path[1];
- /* copy machine name */
- while (*start && *start != '/' && *start != '\\')
- *tmpstart++ = *start++;
+ SKIP_SLASHES(start);
+ COPY_NONSLASHES(tmpstart,start); /* copy machine name */
if (*start) {
- *tmpstart++ = *start;
- start++;
- /* copy share name */
- while (*start && *start != '/' && *start != '\\')
- *tmpstart++ = *start++;
+ *tmpstart++ = *start++;
+ SKIP_SLASHES(start);
+ COPY_NONSLASHES(tmpstart,start); /* copy share name */
}
}
- sep = *start++;
- if (sep == '/' || sep == '\\')
- *tmpstart++ = sep;
*tmpstart = '\0';
- while (sep) {
- /* walk up to slash */
- while (*start && *start != '/' && *start != '\\')
- ++start;
+ while (*start) {
+ /* copy initial slash, if any */
+ if (isSLASH(*start)) {
+ *tmpstart++ = *start++;
+ *tmpstart = '\0';
+ SKIP_SLASHES(start);
+ }
+
+ /* FindFirstFile() expands "." and "..", so we need to pass
+ * those through unmolested */
+ if (*start == '.'
+ && (!start[1] || isSLASH(start[1])
+ || (start[1] == '.' && (!start[2] || isSLASH(start[2])))))
+ {
+ COPY_NONSLASHES(tmpstart,start); /* copy "." or ".." */
+ *tmpstart = '\0';
+ continue;
+ }
+
+ /* if this is the end, bust outta here */
+ if (!*start)
+ break;
- /* discard doubled slashes */
- while (*start && (start[1] == '/' || start[1] == '\\'))
+ /* now we're at a non-slash; walk up to next slash */
+ while (*start && !isSLASH(*start))
++start;
- sep = *start;
/* stop and find full name of component */
+ sep = *start;
*start = '\0';
fhand = FindFirstFile(path,&fdata);
+ *start = sep;
if (fhand != INVALID_HANDLE_VALUE) {
- strcpy(tmpstart, fdata.cFileName);
- tmpstart += strlen(fdata.cFileName);
- if (sep)
- *tmpstart++ = sep;
- *tmpstart = '\0';
- *start++ = sep;
- FindClose(fhand);
+ STRLEN len = strlen(fdata.cFileName);
+ if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) {
+ strcpy(tmpstart, fdata.cFileName);
+ tmpstart += len;
+ FindClose(fhand);
+ }
+ else {
+ FindClose(fhand);
+ errno = ERANGE;
+ return Nullch;
+ }
}
else {
/* failed a step, just return without side effects */
/*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/
- *start = sep;
+ errno = EINVAL;
return Nullch;
}
}