| 10963 | |
| 10964 | |
| 10965 | bool Line::ParseLoopFilePattern(LPTSTR aFilePattern, LoopFilesStruct &lfs, ResultType &aResult) |
| 10966 | // Parse aFilePattern and initialize lfs. |
| 10967 | { |
| 10968 | if (!*aFilePattern) // Some checks below may rely on empty aFilePattern having been excluded. |
| 10969 | // FindFirstFile() would find nothing in this case, but continuing may cause unnecessary |
| 10970 | // rescursion through all subdirectories of the working directory. |
| 10971 | return false; |
| 10972 | // Note: Even if aFilePattern is just a directory (i.e. with no wildcard pattern), it seems best |
| 10973 | // not to append "\\*.*" to it because the pattern might be a script variable that the user wants |
| 10974 | // to conditionally resolve to various things at runtime. In other words, it's valid to have |
| 10975 | // only a single directory be the target of the loop. |
| 10976 | |
| 10977 | // v1.1.31.00: This function was revised. |
| 10978 | // - Resolve aFilePattern to a full path immediately so that changing the working directory |
| 10979 | // does not disrupt recursion or A_LoopFileLongPath/ShortPath. |
| 10980 | // - Take care that path lengths are not limited to below what the system allows. |
| 10981 | // That is, directory+pattern can be up to MAX_PATH on ANSI and 32767 on Unicode, |
| 10982 | // although the latter requires the \\?\ prefix or Windows 10 long path awareness. |
| 10983 | // - Optimize A_LoopFileLongPath and A_LoopFileShortPath by resolving the path prefix |
| 10984 | // up-front and utilizing the names returned by FindFirstFile() during the loop. |
| 10985 | // This can be much faster because file system access is minimized. Benchmarks |
| 10986 | // showed improvement even for single-iteration loops which use A_LoopFileLongPath. |
| 10987 | |
| 10988 | // Prior to v1.1.31.00, A_LoopFileLongPath worked as follows: |
| 10989 | // - Each reference to A_LoopFileLongPath results in two BIV_LoopFileLongPath calls. |
| 10990 | // - BIV_LoopFileLongPath calls GetFullPathName() and ConvertFilespecToCorrectCase(). |
| 10991 | // - CFTCC() calls FindFirstFile() once for each slash-delimited name. |
| 10992 | // - For example, with "c:\foo\bar\baz.txt", A_LoopFileLongPath results in SIX calls to |
| 10993 | // FindFirstFile(). |
| 10994 | |
| 10995 | // Find the final name or pattern component of aFilePattern. |
| 10996 | LPTSTR name_part, cp; |
| 10997 | for (name_part = cp = aFilePattern; *cp; cp++) |
| 10998 | if (*cp == '\\' || *cp == '/') |
| 10999 | name_part = cp + 1; |
| 11000 | |
| 11001 | if (name_part == aFilePattern && *name_part && name_part[1] == ':') // Single character followed by ':' but not '\\' or '/'. |
| 11002 | name_part += 2; |
| 11003 | |
| 11004 | size_t pattern_length = cp - name_part; |
| 11005 | if (pattern_length > _countof(lfs.pattern)) // Most likely too long to match a real path/filename. |
| 11006 | return false; |
| 11007 | tmemcpy(lfs.pattern, name_part, pattern_length + 1); |
| 11008 | lfs.pattern_length = pattern_length; |
| 11009 | |
| 11010 | size_t orig_dir_length = name_part - aFilePattern; |
| 11011 | if (orig_dir_length) |
| 11012 | { |
| 11013 | if ( !(lfs.orig_dir = tmalloc(orig_dir_length + 1)) ) |
| 11014 | { |
| 11015 | aResult = MemoryError(); |
| 11016 | return false; |
| 11017 | } |
| 11018 | tmemcpy(lfs.orig_dir, aFilePattern, orig_dir_length); |
| 11019 | lfs.orig_dir[orig_dir_length] = '\0'; |
| 11020 | lfs.orig_dir_length = orig_dir_length; |
| 11021 | } |
| 11022 | else |
nothing calls this directly
no test coverage detected