#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 |