blob: 2e5fd843a634baba4a4befa9d6995cf1efbf879a [file] [log] [blame]
#ifndef isSLASH
#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
#endif
/* Find the longname of a given path. path is destructively modified.
* It should have space for at least MAX_PATH characters. */
CHAR_T *
LONGPATH(CHAR_T *path)
{
WIN32_FIND_DATA_T fdata;
HANDLE fhand;
CHAR_T tmpbuf[MAX_PATH+1];
CHAR_T *tmpstart = tmpbuf;
CHAR_T *start = path;
CHAR_T sep;
if (!path)
return NULL;
/* drive prefix */
if (isALPHA(path[0]) && path[1] == ':') {
start = path + 2;
*tmpstart++ = toupper(path[0]);
*tmpstart++ = ':';
}
/* UNC prefix */
else if (isSLASH(path[0]) && isSLASH(path[1])) {
start = path + 2;
*tmpstart++ = path[0];
*tmpstart++ = path[1];
SKIP_SLASHES(start);
COPY_NONSLASHES(tmpstart,start); /* copy machine name */
if (*start) {
*tmpstart++ = *start++;
SKIP_SLASHES(start);
COPY_NONSLASHES(tmpstart,start); /* copy share name */
}
}
*tmpstart = '\0';
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;
/* now we're at a non-slash; walk up to next slash */
while (*start && !isSLASH(*start))
++start;
/* stop and find full name of component */
sep = *start;
*start = '\0';
fhand = FN_FINDFIRSTFILE(path,&fdata);
*start = sep;
if (fhand != INVALID_HANDLE_VALUE) {
STRLEN len = FN_STRLEN(fdata.cFileName);
if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) {
FN_STRCPY(tmpstart, fdata.cFileName);
tmpstart += len;
FindClose(fhand);
}
else {
FindClose(fhand);
errno = ERANGE;
return NULL;
}
}
else {
/* failed a step, just return without side effects */
/*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/
errno = EINVAL;
return NULL;
}
}
FN_STRCPY(path,tmpbuf);
return path;
}
#undef CHAR_T
#undef WIN32_FIND_DATA_T
#undef FN_FINDFIRSTFILE
#undef FN_STRLEN
#undef FN_STRCPY
#undef LONGPATH