/* see if a file is normal file or it is a "shell symlink" If directory+filename exists do nothing return false If it doesn't but direstory+filename+".lnk" exists then try to read it. On reading success return true with lnk filename value as resolved. See resolve_shell_shortcut also. > filename: resolving file name < resolved: buffer for resolved path and filename. < result: shell_shortcut_notresolved if file exists or link is invalid. otherwise - shortcut target status */ static shell_shortcut_target_t resolve_shell_symlink (LPCSTR filename, LPSTR resolved) { char pathname[_MAX_PATH]; DWORD fileattr; strcpy(pathname,filename); fileattr = GetFileAttributes(pathname); if (fileattr != 0xFFFFFFFF) return shell_shortcut_notresolved; strcat(pathname,".lnk"); fileattr = GetFileAttributes(pathname); if (fileattr == 0xFFFFFFFF) return shell_shortcut_notresolved; return resolve_shell_shortcut_more(pathname,resolved); }
/* the ultimate shortcut megaresolver style inspired by directory_search_scandir > namein: absolute filename pointing to file or directory wildcards (only asterisk) may appear only as filename < nameout: filename with directory and file shortcuts resolved on failure holds filename resolved so far < result: true if resolving succeeded */ BOOL real_path (LPCSTR namein, LPSTR nameout) { WIN32_FIND_DATA wfd; HANDLE h = NULL; char * nametocheck; char * nametocheck_end; int name_len; /* drive|dir1|dir2|name ^nametocheck ^nametocheck_end */ char saved_char; BOOL next_name = 0;/* if we found an lnk and need to start over */ int try_counter = 33; if ((name_len = strlen(namein)) >= MAX_PATH) return FALSE; strcpy(nameout,namein); do { /* whole file names */ next_name = FALSE; if (!*nameout) return FALSE; /* skip drive or host or first slash */ nametocheck = nameout; if (((*nametocheck >= 'a' && *nametocheck <= 'z') || (*nametocheck >= 'A' && *nametocheck <= 'Z')) && nametocheck[1] == ':') { if (cpslashp(nametocheck[2])) { /* drive */ nametocheck += 3; } else { /* default directory on drive */ char drive[4] = "C:.", *name; int default_len; drive[0] = namein[0]; if (!GetFullPathName(drive,_MAX_PATH,nameout,&name) || (default_len = strlen(nameout)) + name_len >= _MAX_PATH) return FALSE; nameout[default_len] = '\\'; strcpy(nameout + default_len + 1, namein + 2); name_len += default_len - 1; /* Was C:lisp.exe Now C:\clisp\lisp.exe removed 2 added default_len + 1 chars */ nametocheck += default_len + 1; } } else if (nametocheck[0]=='\\' && nametocheck[1]=='\\') { int i; /* host */ nametocheck+=2; for (i=0;i<2;i++) {/* skip host and sharename */ while (*nametocheck && !cpslashp(*nametocheck)) nametocheck++; if (*nametocheck) nametocheck++; else return FALSE; } } else if (cpslashp(*nametocheck)) nametocheck++; /* prefix skipped; start checking */ do { /* each component after just skipped */ int dots_only = 0; int have_stars = 0; /* separate a component */ for (nametocheck_end = nametocheck; *nametocheck_end && !cpslashp(*nametocheck_end); nametocheck_end++); if (*nametocheck_end && nametocheck_end == nametocheck) return FALSE;/* two slashes one after another */ /* save slash or zero */ saved_char = *nametocheck_end; *nametocheck_end = 0; /* Is it . or .. ? FFF handles this strange way */ { char * cp = nametocheck; for (;*cp=='.';cp++); dots_only = !(*cp) && cp > nametocheck; } /* Asterisks in the middle of filename: error Asterisks as pathname: success */ { char * cp = nametocheck; for (;*cp && *cp!='*';cp++); have_stars = *cp == '*'; } if (have_stars && saved_char) return FALSE; if (!have_stars) { if (dots_only || !*nametocheck) { /* treat 'start/./end', 'drive/', 'host/' specially */ /* search for ....\.\* */ char saved[2]; if (nametocheck_end - nameout + 2 > MAX_PATH) return FALSE; saved[0] = nametocheck_end[1]; saved[1] = nametocheck_end[2]; /* !*nametocheck here means there was "something\" before */ strcpy(nametocheck_end,*nametocheck?"\\*":"*"); h = FindFirstFile(nameout,&wfd); nametocheck_end[1] = saved[0]; nametocheck_end[2] = saved[1]; nametocheck_end[0] = 0; if (h != INVALID_HANDLE_VALUE) { FindClose(h); /* don't substitute */ } else return FALSE; /* don't try lnk */ } else {/* not only dots */ h = FindFirstFile(nameout,&wfd); if (h != INVALID_HANDLE_VALUE) { /* make space for full (non 8.3) name component */ int l = strlen(wfd.cFileName), oldl = nametocheck_end - nametocheck, new_name_len = name_len + l - oldl; FindClose(h); if (new_name_len >= _MAX_PATH) return FALSE; if (l != oldl) { int restlen = saved_char?(name_len - (nametocheck_end - nameout)):0; memmove(nametocheck+l,nametocheck_end,restlen+1); } strncpy(nametocheck,wfd.cFileName,l); nametocheck_end = nametocheck + l; name_len = new_name_len; } else {/* try shortcut Note: something\cyglink.lnk doesn't resolve to the contents of cyglink.lnk so one can read/write symlink .lnk files although they are not present in DIRECTORY output. Is it bug or feature? */ char saved[4]; char resolved[MAX_PATH]; int resolved_len; shell_shortcut_target_t rresult; if (nametocheck_end - nameout + 4 > MAX_PATH) return FALSE; strncpy(saved,nametocheck_end+1,4); strncpy(nametocheck_end,".lnk",5); rresult = resolve_shell_shortcut_more(nameout,resolved); strncpy(nametocheck_end+1,saved,4); *nametocheck_end = 0; /* use saved_char as directory indicator */ if (rresult == shell_shortcut_notresolved || rresult == shell_shortcut_notexists || (saved_char ? rresult == shell_shortcut_file : rresult == shell_shortcut_directory)) return FALSE; resolved_len = strlen(resolved); if (saved_char) { /*need to subst nameout..nametocheck-1 with resolved path */ int l2 = name_len - (nametocheck_end - nameout); if (resolved_len + l2 + 2 > MAX_PATH) return FALSE; strncpy(resolved + resolved_len, nametocheck_end + 1, l2); name_len = l2 - 1; } name_len += resolved_len; strcpy(nameout,resolved); next_name = TRUE; } } } if (!next_name) { *nametocheck_end = saved_char; nametocheck = nametocheck_end; if (*nametocheck) nametocheck++; } } while (!next_name && *nametocheck); if (!(--try_counter)) return FALSE; } while (next_name); return TRUE; }
/* the ultimate shortcut megaresolver style inspired by directory_search_scandir > namein: absolute filename pointing to file or directory wildcards (only asterisk) may appear only as filename < nameout: filename with directory and file shortcuts resolved on failure holds filename resolved so far < result: true if resolving succeeded */ BOOL real_path (LPCSTR namein, LPSTR nameout) { WIN32_FIND_DATA wfd; HANDLE h = NULL; char * nametocheck; char * nametocheck_end; /* drive|dir1|dir2|name ^nametocheck ^nametocheck_end */ char saved_char; BOOL next_name = 0;/* if we found an lnk and need to start over */ int try_counter = 33; if (strlen(namein) >= MAX_PATH) return FALSE; strcpy(nameout,namein); do { /* whole file names */ next_name = FALSE; if (!*nameout) return FALSE; /* skip drive or host or first slash */ nametocheck = nameout; if (((*nametocheck >= 'a' && *nametocheck <= 'z') || (*nametocheck >= 'A' && *nametocheck <= 'Z')) && nametocheck[1] == ':' && cpslashp(nametocheck[2])) /* drive */ nametocheck += 3; else if (nametocheck[0]=='\\' && nametocheck[1]=='\\') { int i; /* host */ nametocheck+=2; for (i=0;i<2;i++) {/* skip host and sharename */ while (*nametocheck && !cpslashp(*nametocheck)) nametocheck++; if (*nametocheck) nametocheck++; else return FALSE; } } else if (cpslashp(*nametocheck)) nametocheck++; /* prefix skipped; start checking */ do { /* each component after just skipped */ int dots_only = 0; int have_stars = 0; /* separate a component */ for (nametocheck_end = nametocheck; *nametocheck_end && !cpslashp(*nametocheck_end); nametocheck_end++); if (*nametocheck_end && nametocheck_end == nametocheck) return FALSE;/* two slashes one after another */ /* save slash or zero */ saved_char = *nametocheck_end; *nametocheck_end = 0; /* Is it . or .. ? FFF handles this strange way */ { char * cp = nametocheck; for (;*cp=='.';cp++); dots_only = !(*cp) && cp > nametocheck; } /* Stars in the middle of filename: error Stars as pathname: success */ { char * cp = nametocheck; for (;*cp && *cp!='*';cp++); have_stars = *cp == '*'; } if (have_stars && saved_char) return FALSE; if (!have_stars) { if (dots_only || !*nametocheck) { /* treat 'start/./end', 'drive/', 'host/' specially */ /* search for ....\.\* */ char saved[2]; if (nametocheck_end - nameout + 2 > MAX_PATH) return FALSE; saved[0] = nametocheck_end[1]; saved[1] = nametocheck_end[2]; /* !*nametocheck here means there was "something\" before */ strcpy(nametocheck_end,*nametocheck?"\\*":"*"); h = FindFirstFile(nameout,&wfd); nametocheck_end[1] = saved[0]; nametocheck_end[2] = saved[1]; nametocheck_end[0] = 0; if (h != INVALID_HANDLE_VALUE) { FindClose(h); /* don't substitute */ } else return FALSE; /* don't try lnk */ } else {/* not only dots */ h = FindFirstFile(nameout,&wfd); if (h != INVALID_HANDLE_VALUE) { /* make space for full (non 8.3) name component */ int l = strlen(wfd.cFileName); FindClose(h); if (l != (nametocheck_end - nametocheck)) { int restlen = saved_char?(strlen(nametocheck_end+1) +1/*saved_char*/+1/*zero byte*/) :0; if (nametocheck - nameout + restlen + l + 2 > MAX_PATH) return FALSE; if (restlen) memmove(nametocheck+l,nametocheck_end,restlen); } strncpy(nametocheck,wfd.cFileName,l); nametocheck_end = nametocheck + l; } else {/* try shortcut */ char saved[4]; char resolved[MAX_PATH]; shell_shortcut_target_t rresult; if (nametocheck_end - nameout + 4 > MAX_PATH) return FALSE; strncpy(saved,nametocheck_end+1,4); strncpy(nametocheck_end,".lnk",5); rresult = resolve_shell_shortcut_more(nameout,resolved); strncpy(nametocheck_end+1,saved,4); *nametocheck_end = 0; /* use saved_char as directory indicator */ if (rresult == shell_shortcut_notresolved || rresult == shell_shortcut_notexists || (saved_char ? rresult == shell_shortcut_file : rresult == shell_shortcut_directory)) return FALSE; if (saved_char) { /*need to subst nameout..nametocheck-1 with resolved path */ int l1 = strlen(resolved); int l2 = strlen(nametocheck_end + 1); if (l1 + l2 + 2 > MAX_PATH) return FALSE; strncat(resolved,nametocheck_end + 1,l2+1); } strcpy(nameout,resolved); next_name = TRUE; } } } if (!next_name) { *nametocheck_end = saved_char; nametocheck = nametocheck_end; if (*nametocheck) nametocheck++; } } while (!next_name && *nametocheck); if (!(--try_counter)) return FALSE; } while (next_name); return TRUE; }